为什么在 bash 管道上使用“yes”*不会*导致无限循环?

为什么在 bash 管道上使用“yes”*不会*导致无限循环?

根据其文档,bash 会等到管道中的所有命令完成运行后再继续

shell 在返回值之前等待管道中的所有命令终止。

那么为什么命令会yes | true立即完成呢?难道不应该yes永远循环并导致管道永远不会返回吗?


还有一个子问题:根据POSIX 规范,shell 管道可以选择在最后一个命令完成后返回或等待所有命令完成。从这个意义上说,普通 shell 是否有不同的行为?是否有任何 shellyes | true会永远循环?

答案1

true退出时,管道的读取端关闭,但yes继续尝试写入写入端。这种情况称为“管道损坏”,它会导致内核向 发送信号SIGPIPEyes由于yes对此信号没有做任何特殊处理,它将被终止。如果它忽略了该信号,其write调用将失败并显示错误代码EPIPE。执行此操作的程序必须准备好注意EPIPE并停止写入,否则它们将陷入无限循环。

如果执行strace yes | true1,您可以看到内核为两种可能性做好了准备:

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,信号首先发生。 (从技术上讲,信号是在内核将控制权返回给用户空间之后、但在执行更多机器指令之前传递的,因此writeC 库中的“包装器”函数没有机会设置errno并返回到应用程序。)


1遗憾的是,strace这是 Linux 特定的。大多数现代 Unix 都有一些命令执行类似的操作,但它通常有不同的名称,它可能不会彻底解码系统调用参数,有时它只适用于 root。

答案2

有没有任何贝壳是的 | true 会永远循环吗?

不太可能,因为该yes命令正在使用管道,当管道损坏时它会失败。sleep另一方面,不使用管道,所以:

sleep 100000000 | true

至少会运行 100000000 秒。

相关内容