测试:
function testfun
set input (cat)
echo $input
end
echo testing123 | testfun
这应该输出“ testing123”,但不会产生任何结果。
它在bash中非常有效:
function testfun
{
input="$(cat)"
echo $input
}
echo testing123 | testfun
您可以使用“读取”功能作为解决方法。
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将是不正确的。
抱怨使用某些级别的间接执行时未从终端读取“ subshell”的情况
请注意,所有这些都是关于“全局”命令替换的。 例如在命令行上运行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。
最有用的评论
此问题与
grep
的默认grep函数结合使用会导致很多问题-如果此问题很快无法解决,则可能应删除默认grep别名(或替换为缩写,也许?)至少可以减少出现的次数。使用
cat
作为@milieu的建议似乎对我来说并没有解决此问题,在Fish 2.3.1上(我刚刚意识到这有点落后,但这是为Fedora 25打包的版本)