无法使用 set -eo pipefail 在管道中成功调用任何外部 shell 脚本

Can't successfully call any external shell script in pipe with set -eo pipefail

假设以下test-pipefail.sh批次:

#!/usr/bin/env bash
set -eo pipefail
./echoer.sh | head -n1 >/dev/null
echo "Might work with pipefail"
for i in {1..100} ; do
    ./echoer.sh | head -n1 >/dev/null
done
echo "Stable work with pipefail"

echoer.sh内容:

#!/usr/bin/env bash
echo 'head (GNU coreutils) 8.30'
echo 'GNU bash, version 5.0.16(1)-release (x86_64-pc-linux-gnu)'
exit 0

./test-pipefail.sh的预期结果:

Might work with pipefail
Stable work with pipefail

实际行为:

Might work with pipefail

或(随机)没有输出。

如果我使用任何二进制实用程序而不是 echoer.sh,pipe 中的 Writer 程序永远不会失败,但它总是不起作用(导致 pipefail 脚本到exit) 如果 writer 是 shell 脚本(例如,来自 glibc 二进制包的 ldd)。 将 test-pipefail.sh 中的执行 (./echoer.sh) 替换为采购 (.echoer.sh) 线索 成功执行的可能性增加,即有时我得到

Stable work with pipefail

测试-pipefail.sh输出。

head always returns 在这种管道中取得成功。 删除 echoer.sh 中的第二个 echo 会导致在单独的 shell.

中成功执行采购和执行

我认为结果取决于 writer 完成的速度。如果它完成得非常快,那么它就没有机会与 SIGPIPE.

一起发送

例如:

[STEP 119] # hexdump -n100 /dev/urandom | head -n1; echo '$?'=$?
0000000 eea2 36e7 24d8 15de 620c e258 f9d8 f138
$?=0
[STEP 120] # hexdump -n1000 /dev/urandom | head -n1; echo '$?'=$?
0000000 cf81 dd51 1594 88b2 c9c1 6c8a bbbd c80f
$?=0
[STEP 121] # hexdump -n1000 /dev/urandom | head -n1; echo '$?'=$?
0000000 ef2d b2d3 1024 af9f ee1e a5e6 5528 699e
$?=0
[STEP 122] # hexdump -n2000 /dev/urandom | head -n1; echo '$?'=$?
0000000 d9f7 6a0d 633b c1f7 8928 cef8 3ea9 6f5a
$?=141
[STEP 123] # hexdump -n2000 /dev/urandom | head -n1; echo '$?'=$?
0000000 c044 dbb0 c227 1836 9fb5 f03b b2d1 0605
$?=141
[STEP 124] #

让我们把问题归结为最基本的问题。考虑:

$ (set -o pipefail; cat /dev/zero | head -c10; declare -p PIPESTATUS)
declare -a PIPESTATUS=([0]="141" [1]="0")

发生的事情是,当 head 填满时,它完成,关闭管道。前面的命令,在本例中为 cat,获取 SIGPIPE (13) 信号。因此,它将其退出代码设置为 128+13=141 以指示失败。

所以,问题是当第二个进程 head 完成时,第一个进程是否仍然 运行。有时,您的 echoer.shhead 快,有时又慢。

由于我们同时处理两个进程,所以时间总是可变的。

采购与执行

Replacing execution (./echoer.sh) in test-pipefail.sh with sourcing (. echoer.sh) leads to increased probability of successful execution

采购消除了初始化新 shell 的需要,这可能会导致执行速度更快,因此更有可能在 head.

之前完成

二进制程序

Writer program in pipe never fails if I'm using any binary utility instead of echoer.sh

我上面的 cat 示例显示了相反的情况。这是因为 cat /dev/zero 程序将 永远不会 完成,从而确保它最终会收到 SIGPIPE。