管道 shell 脚本的 Stderr 并不总是显示

管道 shell 脚本的 Stderr 并不总是显示

我通过管道传输自己的 shell 脚本进行一些测试,无意中发现了一些奇怪的东西。也就是说,这些管道进程的 stderr 并不总是显示在屏幕上。

我简化了脚本,这是我与 bash 的会话:

$ cat file1
echo stdout
echo stderr 1>&2
$ cat file2
echo stdout2
echo stderr2 1>&2
$ cat file3
echo stdout3
echo stderr3 1>&2

$ ./file1 | ./file2 | ./file3
stderr2
stderr
stdout3
stderr3

$ ./file1 | ./file2 | ./file3
stderr
stdout3
stderr3

$ ./file1 | ./file2 | ./file3
stderr2
stdout3
stderr3

我知道前两个脚本的标准输出丢失了,因为 file3 没有读取任何内容,但这并不重要。

在第二种和第三种情况下,stderr2和输出分别发生了什么?stderr

答案1

哦,竞争条件!为了产生这种效果,幕后发生了一些事情。

首先,如果一个进程尝试写入另一端已关闭的管道,则该进程会收到一个SIGPIPE信号。默认情况下,这将结束该过程。你为什么想要这个?如果运行cat my_huge_file | head -3,您只会看到文件的前三行,并且由于信号机制,cat知道在这三行之后停止。如果没有它,它将读取整个文件。

其次,管道中的所有进程都是并行运行的,哪个进程将首先完成是不可预测的。当进程结束时,其所有文件描述符都将关闭 - 包括任何管道的读取端。

因此,有时会发生这样的情况,例如,在写入标准输出file3之前就完全完成了。file2然后,当file2尝试写入时,它会得到SIGPIPE,并在未到达该行的情况下退出echo stderr2。但其他时候,在完成file2之前设法写入标准输出file3,在这种情况下它可以继续。

相关内容