【问题标题】:Why does this shell pipeline exit?为什么这个 shell 管道会退出?
【发布时间】:2012-04-19 08:31:02
【问题描述】:

我有一个随机生成 10 个字符密码的 shell 管道:

cat /dev/urandom | base64 | head -c 10

我的问题是cat /dev/urandom | base64 是一个不会自行退出的无限输出流。但是为什么附加head -c 10 会使整个管道退出?我假设catbase64head 是3 个独立的进程,head 如何使cat 进程退出?

【问题讨论】:

    标签: linux bash unix pipe


    【解决方案1】:

    head 在读取所需数量后关闭输入文件。当管道从一侧关闭时,另一侧会出现写入错误;这会导致base64 关闭,进而导致cat 关闭。

    【讨论】:

    • 可能值得一提的是,head 获得任何输入的唯一原因是base64 在获得一定数量的输入后写入输出,即当其缓冲区已满时。如果它要读到 EOF,它将永远读下去,而head 将永远无法破解它。所以类似的管道,比如cat /dev/urandom | sum | head -c 10 会表现不同,因为sum 等待EOF。
    • Rob 的评论非常相关。如果进程继承了 SIGPIPE 处理程序或忽略 SIGPIPE(例如,如果它在较旧的 python 解释器子进程模块下运行)并且不检查写入错误,它将不会终止。写入错误和接收 SIGPIPE 之间存在巨大的差异,忽略这两个问题的程序很容易无限期地运行。
    • @WilliamPursell:进程不能继承 SIGPIPE 处理程序。它可以继承 SIG_IGN 非处理程序,但是当进程使用exec() 替换自己时,任何信号处理程序都会重置为默认值。不难看出原因:在旧进程中指向函数的指针几乎肯定不是新进程的好选择。但是您是对的,忽略写入错误并同时忽略 SIGPIPE 的进程将继续工作太久。
    【解决方案2】:

    base64 输出 10 个字节后,head 获得足够的输入并退出。前者尝试输出更多字节时,会收到SIGPIPE signal,因此也退出。同理,cat会依次退出。

    【讨论】:

      【解决方案3】:

      管道通过将一个进程 A 的输出连接到 B 的输入来工作。连接可以在以下情况下断开

      • A 关闭其输出。 B 将获得 EOF。
      • B 关闭其输入。 A 在尝试写入下一个字节时会收到输出不再可用的错误。

      由于这两种情况很常见,因此处理已移至 C 标准库中。

      【讨论】:

      • 谢谢,但“处理已移至 C 标准库”是什么意思? A 和 B 是否被 shell 终止,而不是它们检测到输入/输出的关闭并自行停止?
      • 当 B 关闭其一侧的管道时,A 将收到一个信号。标准库 c.lib (fprintf(), open(), read(), ...) 的 I/O 例程中的代码处理信号并且函数调用将返回返回 errno EPIPE = "Broken pipe" .
      • @AaronDigulla,我不同意。我不知道任何安装 SIGPIPE 处理程序的 C 库,我的实验也没有建议。事实上,SIGPIPE 的交付会导致任何尚未安装处理程序的程序(即所有程序的 99%)退出。
      • 正确,Rob,它是 SIGPIPE。谢谢!你能创造一个答案吗?我会接受的。目前,这 2 个答案并不完全正确。
      • @Robᵩ:我的意思是你在控制台中看不到“Broken Pipe”错误,所以必须有人处理这个信号。
      猜你喜欢
      • 2018-05-27
      • 2018-02-13
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      • 2014-11-01
      • 2020-11-05
      • 2011-09-18
      • 2014-09-04
      相关资源
      最近更新 更多