我在 Linux Mint 中的 Bash 中。如果我执行
{ echo 1 || >&2 echo "[ $? ]" ; } | echo
会打印一个空行。请注意,Bash 会将其解释echo
为内置命令。但如果我执行
{ /bin/echo 1 || >&2 echo "[ $? ]" ; } | /bin/echo
它输出
[ 141 ]
我们知道man bash
141 表示它接收到了信号 13,并且man 7 signal
表示生成了 SIGPIPE 信号。man 7 pipe
我们读到
[i]如果引用管道读取端的所有文件描述符都已关闭,则将
write(2)
导致为调用进程生成 SIGPIPE 信号。
因此,我得出结论,在第二种情况下,“所有引用管道读取端的文件描述符都已关闭”,无论它对 意味着什么/bin/echo
。但是为什么在 Bash 的情况下输出不同echo
?
在
{ echo 1 || >&2 echo "[ $? ]" ; }
并且echo
必须同时运行,因此在单独的进程中。该
echo 1 || >&2 echo "[ $? ]"
代码正在由子 shell 进程评估和运行。就像
echo
内置的一样,当它write(1, "1\n", 2)
在断开的管道上打开 fd 1 时,它是被 SIGPIPE 杀死的 shell 进程,因此它无法继续执行该>&2 echo...
部分,因为它已经死了。如果将其更改为:
(还添加了一个
sleep 1
以确保在发生时管道已经断开echo 1
),然后因为 SIGPIPE 在管道左侧的子 shell 中被忽略,所以write()
返回一个EPIPE
错误,该错误echo
会报告并报告1
退出状态。在
正在运行的子 shell
/bin/echo 1 || >&2 echo "[ $? ]"
派生出一个子进程,在该子进程中执行echo
(然后等待其终止),该进程执行write(1, "1\n", 2)
并被终止。然后等待返回,并指示执行的进程/bin/echo
已通过 SIGPIPE 终止(结果为$?
141),并且子 shell 运行>&2 echo...
。