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

nyx*_*x19 3 linux bash shell pipe built-in

假设以下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"
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

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

Might work with pipefail
Stable work with pipefail
Run Code Online (Sandbox Code Playgroud)

实际行为:

Might work with pipefail
Run Code Online (Sandbox Code Playgroud)

或(随机)没有输出。

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

Stable work with pipefail
Run Code Online (Sandbox Code Playgroud)

test-pipefail.sh输出中。

head 在这样的管道中总是返回成功。删除echoer.sh 中的第二个 echo会导致成功执行,同时在单独的 shell 中进行采购和执行。

Joh*_*024 6

让我们将问题归结为基本问题。考虑:

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

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

所以,问题是当第二个进程head完成时,第一个进程是否仍在运行。有时,你echoer.sh的速度比head,有时慢。

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

采购与执行

./echoer.shtest-pipefail.sh 中的execution ( ) 替换为 sourcing ( . echoer.sh) 会增加成功执行的概率

采购消除了初始化新 shell 的需要,这可能会导致更快的执行,因此更有可能提前完成head

二进制程序

如果我使用任何二进制实用程序而不是管道中的 Writer 程序永远不会失败 echoer.sh

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


pyn*_*exj 5

我认为结果取决于作者完成的速度。如果它完成得非常快,那么它就没有机会与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] #
Run Code Online (Sandbox Code Playgroud)