一个例子应该可以澄清我的问题。这种行为对我来说很有意义:
$ echo hi | cat
hi
$ echo hi | tee >(cat)
hi
hi
第一种情况是显而易见的。在第二种情况下,我们使用命令替换将“hi”通过管道传输到 tee 中,一个“hi”由tee
'd打印,而另一个“hi”由' 传递管道cat
打印。tee
到目前为止,一切都很好...
但是在这种情况下第一个“hi”会发生什么:
$ echo hi | tee >(echo yo)
yo
返回代码是 141,管道失败。可能是什么原因造成的?
我正在默认终端应用程序中运行 Mac OSX El Capitain、bash
答案1
我想我已经找到了如何调整您的体验,将其变成其他人能够重现的东西:
$ (回声你好;睡眠1;回声世界)| T 恤 >(猫) 你好 你好 ......并且,在短暂的延迟之后, 世界 世界 $ 回显“$?” 0 $ (回声你好;睡眠1;回声世界)| T 恤 >(回声哟) 哟 你好 $ 回显“$?” 141
正如您希望理解的那样, 创建一个通往正在运行的进程的管道>(command)
command
。标准输入为command
连接到命令行上的其他命令(在本例中为tee
)可以打开和写入的路径名。什么时候command
是cat
,该进程坐在那里并从 stdin 读取,直到获得 EOF。在这种情况下,tee
将从标准输入读取的所有数据写入管道都没有问题。
但当command
是echo yo
,进程写入yo
标准输出并立即退出。这会导致一个问题tee
;当它写入另一端没有进程的管道时,它会收到 SIGPIPE 信号。
显然,OS X 的版本tee
首先在命令行上写入文件,然后是其标准输出。因此,在您的示例 ( echo hi | tee >(echo yo)
) 中,
tee
在第一次写入时就出现管道故障。然而,Linux 和 Cygwin 上的版本tee
写入标准输出第一的,因此它能够hi
在死亡之前向屏幕写入内容。在我的增强示例中,tee
当它写入hello
管道时就会死亡,因此它没有机会读取和写入world
.
答案2
要直观地了解正在发生的情况,请比较以下两种变体:
bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'
bash -c 'wait_and_tee () { sleep 1; tee "$@"; };
echo hi | wait_and_tee >(echo yo); echo $?'
注意到第一个变体中发生了什么吗?
$ bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'
hi
0
$ yo
进程替换中的命令sleep 1; echo yo
与外部的命令并行执行,bash 不会等待它完成。所以事件的顺序是:
echo hi
、sleep 1; echo yo
和三个命令tee
并行启动。echo hi
写入hi
其输出(|
管道)。tee
从管道读取并写入其标准输出及其命令行参数,这是由>(…)
. 创建的另一个管道。这会导致一份副本hi
打印到终端,一份副本保存在>(…)
管道的缓冲区中。- 与前面的要点并行,
echo hi
退出,它关闭管道的写入端|
。 tee
注意到它已到达输入文件的末尾。它已写出所有数据,因此退出。- 从bash的角度来看,管道两边都已经退出,所以命令结束了。由于
tee
返回0,管道的状态为0。 - 一秒后,
sleep 1
结束,echo yo
执行。
在第二种变体中,我通过强制它在开始之前终止来强制终止echo yo
before 。这次,事件的顺序是:tee
tee
echo hi
、echo yo
和三个命令sleep 1
并行启动。echo hi
写入hi
其输出(|
管道)。- 与前一个要点并行,
echo yo
打印yo
并退出。 - 一秒钟后,
sleep 1
退出并tee
启动。 tee
从其输入中读取hi
,并尝试将其写入终端(其标准输出)和作为>(…)
参数传递的管道。由于打开此管道进行读取的进程 (echo yo
) 一秒钟前退出,因此尝试写入管道失败,并显示信号管道(信号13,其中shell 报告为 128+signal_number)。
正如 G-Man 所解释的,是否hi
在第二种情况下显示取决于是否tee
尝试首先写入其标准输出或其文件参数。
如果没有sleep
调用,计时可能会以任何一种方式进行(我在 Linux 下得到大约一半/一半,不同的内核可能会使一个计时比另一个计时更有可能)。