根据其文档,bash 会等到管道中的所有命令完成运行后再继续
shell 在返回值之前等待管道中的所有命令终止。
那么为什么命令会yes | true
立即完成呢?难道不应该yes
永远循环并导致管道永远不会返回吗?
还有一个子问题:根据POSIX 规范,shell 管道可以选择在最后一个命令完成后返回或等待所有命令完成。从这个意义上说,普通 shell 是否有不同的行为?是否有任何 shellyes | true
会永远循环?
答案1
当true
退出时,管道的读取端关闭,但yes
继续尝试写入写入端。这种情况称为“管道损坏”,它会导致内核向 发送信号SIGPIPE
。yes
由于yes
对此信号没有做任何特殊处理,它将被终止。如果它忽略了该信号,其write
调用将失败并显示错误代码EPIPE
。执行此操作的程序必须准备好注意EPIPE
并停止写入,否则它们将陷入无限循环。
如果执行strace yes | true
1,您可以看到内核为两种可能性做好了准备:
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++
strace
通过调试器 API 监视事件,它首先告诉它系统调用返回错误,然后告诉它信号。但从 的角度来看yes
,信号首先发生。 (从技术上讲,信号是在内核将控制权返回给用户空间之后、但在执行更多机器指令之前传递的,因此write
C 库中的“包装器”函数没有机会设置errno
并返回到应用程序。)
1遗憾的是,strace
这是 Linux 特定的。大多数现代 Unix 都有一些命令执行类似的操作,但它通常有不同的名称,它可能不会彻底解码系统调用参数,有时它只适用于 root。
答案2
有没有任何贝壳是的 | true 会永远循环吗?
不太可能,因为该yes
命令正在使用管道,当管道损坏时它会失败。sleep
另一方面,不使用管道,所以:
sleep 100000000 | true
至少会运行 100000000 秒。