当我将"exit"命令通过管道传输给"tee"命令时,为什么脚本没有退出?

3
我正在运行一个脚本,使用tee命令将输出结果同时显示在屏幕和文件中:
{ echo "hello world" ; exit 1; } | tee -a logfile.log

exit不起作用。我无法退出我的脚本。当我删除tee时,它可以正常工作。为什么会这样?


1
“exit” 实际上是有效的。尝试使用 { echo "hello world" ; exit 1; echo xyzzy; }| tee -a logfile.log,你会发现 xyzzy 不会被写入到标准输出或文件中。在像 command1 | command2 这样的结构中,每个命令都在自己的子 shell 中执行。(如果启用了 lastpipe 选项,则最后一个命令可能在当前 shell 中运行) - M. Nejat Aydin
当您想要退出运行管道的进程时,为什么不将“exit”作为管道后面的下一条语句呢? - user1934428
2个回答

1

如其他地方所述(包括有点间接的BashFAQ#24),管道会创建子shell,因此您的exit只会退出子shell。

您可以通过使用重定向到进程替换来避免这种情况:

{ echo "hello world"; exit 1; } > >(tee -a logfile.log)

它正在运作!谢谢!我从未想过重定向可以做到这一点。 - linklin
但我也不明白,在我的脚本中,退出在管道之前,那么为什么退出在子shell或父shell中起作用? - linklin
@linklin,当你将{ echo "hello world"; exit 1; }放在大括号中,并将其输出管道传递给其他内容时,该命令组成为单个管道元素,在单个子shell中运行;因此,exit退出该子shell(它是管道的一部分),而不是整个shell。 - Charles Duffy

0
这是由于管道为每个组件派生一个子shell,因此exit运行在这个新的shell中。
你应该像这样重新编写脚本:
echo "hello world" | tee -a logfile.log
exit 1

3
产生子shell的不是括号,而是|管道符号。 - John Kugelman
2
我已经编辑过这段内容,以尝试提高准确性。 (它也不是“一个新的bash实例”,而是一个分叉的子shell;在fork()期间复制本地变量和其他状态,在execve()系统调用期间丢失,并因此不存在于真正的新shell中)。 - Charles Duffy

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