Fish-shell: 无法读取管道以起作用

创建于 2012-07-05  ·  21评论  ·  资料来源: fish-shell/fish-shell

测试:

function testfun
    set input (cat)
    echo $input
end

echo testing123 | testfun

这应该输出“ testing123”,但不会产生任何结果。

它在bash中非常有效:

function testfun
{
    input="$(cat)"
    echo $input
}

echo testing123 | testfun

最有用的评论

此问题与grep的默认grep函数结合使用会导致很多问题-如果此问题很快无法解决,则可能应删除默认grep别名(或替换为缩写,也许?)至少可以减少出现的次数。

使用cat作为@milieu的建议似乎对我来说并没有解决此问题,在Fish 2.3.1上(我刚刚意识到这有点落后,但这是为Fedora 25打包的版本)

所有21条评论

您可以使用“读取”功能作为解决方法。

function t
    while read -l line
        echo $line
    end
end

我只是明白,在鱼中它是行不通的,因为stdin通过管道传递给“ set”而不是“ cat”。

这个问题比仅仅以某种方式“设定”工作要深得多。 完全将功能管道弄乱了。 发送到函数中的终端I / O确实可以工作,但是仍然有些怪异-它似乎可以缓冲输入并立即交付所有输入。 观察此函数会发生什么:

~> function meh
       cat
   end
~> # First, the way it's supposed to work.
~> # As input, we press the keys: a RET b RET control-D
~> cat
a
a
b
b
~> cat | cat
a
a
b
b
~> # Now...
~> meh
a
a
b
b
~> # So far so good, but...
~> cat | meh
a
b
^D
... um...
^D
control-D repeatedly does not work
try control-C
Job 1, “cat | meh” has stopped
~> fg
Send job 1, “cat | meh” to foreground
cat: stdin: Interrupted system call
~> jobs
jobs: There are no jobs
~> # Dear lord.
~> # For completeness...
~> meh | cat
a
b
aD
b
~> 

此外, cat | meh | cat的工作方式一样, cat | begin; cat; end
我可以进一步告诉您,抱怨cat | meh中的系统调用中断的“ cat”是第一个“ cat”。 那是:

~> cp /bin/cat mycat
~> ./mycat | meh
Job 1, “./mycat | meh” has stopped  #after control-C
~> fg
Send job 1, “./mycat | meh” to foreground
mycat: stdin: Interrupted system call

就是这样。 显然,这与fish如何调用函数以及如何在其中构造管道有关。 有人碰巧知道吗?

好吧,我发现正在跑步
pbpaste | begin; cat; end
在剪贴板上为“ 23 \ n”的新鲜鱼壳中重复多次,有时只会将23打印回去,有时会导致壳锁定,此时control-C不能执行任何操作。 我认为这一定是某种竞赛条件。 好家伙。

同时,信号SIGTTIN似乎以./mycat | begin; cat; end发送到“ mycat”:

     21    SIGTTIN      stop process         background read attempted from
                                             control terminal

然后,根据GNU libc手册:“一个进程在作为后台作业运行时无法从用户终端读取。当后台作业中的任何进程试图从终端读取时,该作业中的所有进程都将被发送。 SIGTTIN信号。”

因此,看起来“ mycat”要么在后台启动,要么先启动,然后放入后台,然后再将其通过管道传递到fish函数类事物中。 也许这些知识会有所帮助。

显然,这会在管道的两面都有背景...但是给出fg命令会使过程从后台拉出,从而使其能够按预期工作。

~ $ alias pjson='python -m json.tool | pygmentize -l json'
~ $ curl -u smoku -X GET -H "Content-Type: application/json" 'https://jira.......' | pjson
Job 4, 'curl -u smoku -X GET…' has stopped
~ $ fg
Enter host password for user 'smoku': ********
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   593    0   593    0     0   1372      0 --:--:-- --:--:-- --:--:--  1375
~ $ fg
{
    "expand": "renderedFields,names,schema,transitions,operations,editmeta,changelog",
    "id": "29874"
}

有点烦人,我需要在$PATH创建pjson包装脚本,而不是简单的别名... :(

供我参考,这也是openSUSE错误https://bugzilla.opensuse.org/show_bug.cgi?id=963548

好极了! 我想我找到了解决方法! 感谢gustafj在第110期中的评论解释了鱼的管道语法,我提出了以下建议:

function line --argument-names n
    cat 1>| tail -n +$n | head -1
end

此问题与grep的默认grep函数结合使用会导致很多问题-如果此问题很快无法解决,则可能应删除默认grep别名(或替换为缩写,也许?)至少可以减少出现的次数。

使用cat作为@milieu的建议似乎对我来说并没有解决此问题,在Fish 2.3.1上(我刚刚意识到这有点落后,但这是为Fedora 25打包的版本)

外壳程序和命令行之间的执行似乎有所不同:

(zsh)$ ./fish -c "read n | grep nothing"    
read> lol
(zsh)$ ./fish
(fish)$ read n | grep nothing
read> 
# Stuck forever, needs to kill the terminal. ^C, ^Z have no impact.

也许这可以帮助调试问题?

@layus :不,这是#3805,这是鱼本身无法控制终端的问题。

_我认为这方面的某些原始行为已更改,以下是关于fish master / 3.0_

鱼在这里出错有两个基本问题,第一个是缓冲函数/块输出(我很确定现代的shell不会在任何地方缓冲

通常,您有容易使用的外部命令(和有效地使用相同方式处理的内置命令):一个输入,两个输出,其中一个可以链接到后续命令,另一个必须重定向到文件或tty。 但是块和函数是棘手的,因为您基本上是将输入(因为只能有一个)映射到一系列(最终将扩展为)外部命令或内建函数。

也就是说,我不同意当前的行为是错误的。 (cat)不应读取通过管道传递到执行命令的数据。

mqudsi<strong i="11">@ZBook</strong> /m/c/U/m/Documents> type testfun
testfun is a function with definition
function testfun
    set input (cat)
    printf "You said '%s'\n" $input
end
mqudsi<strong i="12">@ZBook</strong> ~/r/fish-shell> echo testing123 | testfun
hello
^D
You said 'hello'

您正在将输入管道传递到块中,无论set是消耗输入,消耗部分输入还是完全忽略输入,cat正确地连接到/dev/tty作为输入,然后正确地传递给了替换为命令行的shell。 实际上,有(很多)针对此存储库的错误,它们抱怨在使用某些级别的间接执行时,“子外壳”无法从终端读取的情况。 恕我直言,这是bash在这里被打破,尤其是因为bash支持真正的子shell并在此处提供异步性。

我要说的唯一损坏的行为是由于在功能/块中启动了外部命令而没有完全消耗输入的情况:

mqudsi<strong i="19">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        head -n1 | read -l line1
                                        head -n2 | read -l line2
                                        echo line1: $line1
                                        echo line2: $line2
                                    end
line1: foo
line2:

TBH我很惊讶,但这可以正常工作:

mqudsi<strong i="23">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        /bin/echo 'hi from echo'
                                        cat | read -z from_cat
                                        printf 'from_cat: "%s"' $from_cat
                                    end
hi from echo
from_cat: "foo
bar
"¶  

这也是正确的:

mqudsi<strong i="27">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        cat | read -zl from_cat1
                                        cat | read -zl from_cat2
                                        printf 'from_cat1: "%s"\n' $from_cat1
                                        printf 'from_cat2: "%s"\n' $from_cat2
                                    end
from_cat1: "foo
bar
"
from_cat2: ""

尤其是考虑到某天计划在异步执行的鱼中引入真实的子壳的计划,我会说,就此处报道的原始案例而言,鱼的行为是正确的。 实际上,除非有人反对并且可以在这里提出令人信服的论点,否则我倾向于完全解决此问题。

虽然原始的错误报告无效,但是@waterhouse提出的问题很明显,而且很cat | meh案。

mqudsi<strong i="8">@ZBook</strong> ~/r/fish-shell> cat | meh
a
a
b
b
^D
mqudsi<strong i="9">@ZBook</strong> ~/r/fish-shell>

也就是说,我不同意当前的行为是错误的。 (cat)不应读取通过管道传递到执行命令的数据。

我对此非常不同意!

cat正确连接到/ dev / tty进行输入

这是心理模型的问题。 我会说cat连接到“当前标准输入”作为输入。 如果函数或块未重定向,那就是tty。 如果重定向,就是这样! 因此,在此处连接到/ dev / tty将是不正确的。

抱怨使用某些级别的间接执行时未从终端读取“ subshel​​l”的情况

请注意,所有这些都是关于“全局”命令替换的。 例如在命令行上运行echo (fzf) 。 在这种情况下,没有标准输入。

所以我想说的将是这样的:

echo | echo (cat) # from tty

begin
   echo | echo (cat) # from file
end < file

在这种情况下,有一个相关的问题(#1035)询问有关stderr的问题,并且该问题没有被重定向。 对于旧的math函数,这确实是个问题,因为它恰好在其中具有命令替换功能,因此您无法重定向它。

这是它的标准输入部分。 如果一个函数只执行裸(cat) ,那么始终从tty中读取该值真的有用吗? 还是在这种情况下不能只使用</dev/tty

有趣的想法。

我猜可以归结为括号是否表示简单替换(即“假装括号的内容在上面的行上,将其运行到完成位置,将结果存储在变量中,然后在此处替换变量”),或者是否重新(当前已损坏)子壳。 我认为人们的共识是,鱼缺少适当的子壳支持,但目的始终是“在某个时候”修复它。

如果是前者,那么是的,我同意,当前行为已被破坏,因为如果将括号的内容移到另一行,则肯定应该从重定向到该块的输入中读取它。

但是,次外层是一个强大的概念THA的是,他们让你做的事情,是无法实现的命令替换,创造更为敏感,并且能够脚本。 从技术上讲,可以将输入到块中的任何内容连接到在子shell中执行的命令的stdin,但我认为这与那里的思维模型不兼容。

括号是否表示简单替换(即“假装括号的内容在上面的行上,运行它们直到完成,将结果存储在变量中,然后在此处替换变量”)还是它们是(当前已损坏的)子shell 。

我认为这些术语的定义不够清晰,因此在这里没有太多用处。

对我来说,这取决于更自然,更典型和更有用的东西。

从终端读取肯定是有用的,有时即使您有另一个标准输入,您仍想从终端读取(例如, fzf基本上是专门这样做的)。

但是我认为从stdin读取要更为典型,特别是考虑到非交互式使用根本不会从tty读取。 并且由于仍然可以从tty中读取(通过</dev/tty重定向),因此可以将其保留为第二选项。

我建议的模型中没有与</dev/tty相反的事实,这使我重新考虑自己的立场。

我可能不够深入,无法完全理解讨论。 但是我需要解决一些问题,我想知道是否需要bash脚本来解决它。

这基本上是一个非常简单的任务:我想通过pv将(z)cat的stdout通过pv传递到mysql cli(基本上是为了恢复备份),并且因为我不想输入连接字符串,所以我想使用一个函数:

function mysqlenv --description connect to mysql server using config from .env
  mysql -u (getEnv DB_USERNAME) -p(getEnv DB_PASSWORD) (getEnv DB_DATABASE)
end

首先,我确定这是可行的,因为命令的标准输入显然是从左侧的命令输出的,但现在我很困惑。 好的,mysqlenv不是命令,而是函数。 现在,我在这里阅读了很多文本和很多“这应该工作”,但没有任何工作。

我试过的

  • cat -|mysql...无输出; mysql没有得到输入; ctr + c存在mysql; 管道在后台运行
  • mysql... <&0无输出; mysql没有得到输入; ctr + c存在mysql; 管道在后台运行
  • set input (cat); mysql...无输出; mysql没有得到输入; ctr + c全部存在; 什么都没有保留在背景中
  • read -z|mysql...无输出; mysql没有得到输入; ctr + c打印^c

再次是我的命令提示符: zcat some_backup.sql.gz|pv -s (zsize some_backup.sql.gz)|mysqlenv 。 当它直接与mysql一起使用时(在它们之间没有fish函数),它显示了管道状态-因此它应该可以工作。

那么,如何在函数内部将stdin从函数传递到命令的stdin?

不要说我必须通过while read...重新连接每一行。 它可能可以工作,但不是解决方案,因为它处理起来太慢。

@tflori :简单得多。 只需按原样保留命令,无需任何重定向。 问题不在于直接在函数中的命令。 就像是

function foo
    cat
end

作品。 cat像应该的那样获得标准输入。

它不是在命令替换中,而是在您遇到此错误时。

@faho意味着初始功能应该起作用? 但事实并非如此。 也许我的版本已经过时了? 我目前正在使用2.7.1

也许我的版本已经过时了? 我目前正在使用2.7.1

@tflori :是的,您需要3.0.2。

此页面是否有帮助?
0 / 5 - 0 等级