Shell管道:当一个命令失败时立即退出

Tob*_*obi 23 bash pipe

我在bash中使用了几个命令的管道.如果其中一个命令失败,有没有办法配置bash立即终止整个管道中的所有命令?

在我的例子中,第一个命令command1运行一段时间直到产生一些输出.您可以替代command1(sleep 5 && echo "Hello"),例如.

现在,command1 | false确实在5秒后失败但不是立即失败.

此行为似乎与命令生成的输出量有关.例如,find / | false立即返回.

一般来说,我想知道为什么bash表现得像这样.任何人都可以想象任何情况下,像这样的代码command1 | non-existing-command不会立即退出吗?

PS:使用临时文件对我来说不是一个选项,因为我管道的中间结果很大,无法存储.

PPS:似乎set -eset -o pipefail没有影响这种现象.

Mar*_*l M 16

bash文档在其有关管道的部分中说明:

管道中的每个命令都在自己的子shell中执行[...]

"在它自己的子shell中"意味着产生了一个新的bash进程,然后执行实际的命令.每个子shell都会成功启动,即使它立即确定要求执行的命令不存在.

这解释了为什么即使其中一个命令是无意义的,也可以成功设置整个管道.Bash不会检查是否可以运行每个命令,它会将其委托给子shell.这也解释了为什么,例如,该命令nonexisting-command | touch hello将抛出"未找到命令"错误,但hello仍将创建该文件.

在同一部分,它还说:

shell在返回值之前等待管道中的所有命令终止.

sleep 5 | nonexisting-command,如AH指出的,sleep 5终止5秒后,不立即,进而罩体也将等待5秒钟.

我不知道为什么实现是这样做的.在像你这样的情况下,这种行为肯定不像人们期望的那样.

无论如何,一个稍微丑陋的解决方法是使用FIFO:

mkfifo myfifo
./long-running-script.sh > myfifo &
whoops-a-typo < myfifo
Run Code Online (Sandbox Code Playgroud)

在这里,long-running-script.sh启动然后脚本在下一行立即失败.使用多个FIFO,可以扩展到具有两个以上命令的管道.