在一个管道中如何退出BASH无限循环

6

我遇到了BASH无限循环的一个有点奇怪的行为,输出被管道传输到了另一个进程中。也就是说,我运行了以下两个命令:

(while true; do echo xxx; done) | head -n 1
(while true; do date; done) | head -n 1

第一个立即退出,而第二个不会(我认为它将永远运行而不被杀死)。我还尝试了一个隐式的无限循环:

yes | head -n 1

它也能自己退出。每种情况下,屏幕上都会立即打印出适当的输出行。我只是好奇是什么决定了这样的命令何时结束。


2
请注意,括号是不必要的;“while”循环本身就是一个完整的命令,可以作为管道的左侧。 - chepner
1个回答

7

head退出时,括号表达式的标准输出被关闭。如果使用类似于date的外部命令,循环会挂起。如果使用bash的内部命令,例如echo,则循环将退出。要进行验证,请使用:

(while true; do /bin/echo xxx; done) | head -n 1

如果您使用的话,它会挂起。

(while true; do date; echo $? 1>&2; sleep 1; done) | head -n 1

您会发现在第二轮中,date命令返回错误的退出代码,即不为零的某些内容。显然,Bash对此并不像内部命令出现问题时那样认真对待。我想知道这是有意还是Bash中的一个错误。
为了确保循环退出,似乎可以采用以下方法:
(set -e; while true; do date ; done) | head -n 1

谢谢,我明白了。 - Tomasz Żuk
3
唯一存在的进程是试图写入已关闭文件句柄的进程,该文件句柄是在“head”退出时留下的。当外部程序“date”成为“受害者”时,“bash” shell 将继续不断地生成尝试写入和接收“SIGPIPE”的“date”进程。对于内置命令,是“bash” shell 本身接收到了“SIGPIPE”,因此它会退出并终止管道。 - chepner
set -e 能够生效是因为当 date 第一次尝试写入已关闭的文件句柄时,它会以非零状态退出,这将导致运行 while 循环的 bash shell 也根据 -e 选项退出。 - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接