如何在bash中抑制SIGPIPE?

如何在bash中抑制SIGPIPE?

我正在尝试执行以下代码:

set -euxo pipefail
yes phrase | make installer

其中Makefile使用phrase标准输入来创建安装程序文件。但是,此命令以错误代码 141 结束,这破坏了我的 CI 构建。这个例子可以简化为:

yes | tee >(echo yo)

从这里可以看出:当管道输出到三通时管道失败 (141) - 为什么?- 这个错误意味着管道消费者刚刚停止消耗输出 - 这在我的情况下完全没问题。

有没有办法抑制管道错误,并只从中获取返回码make installer

答案1

141 退出代码表示进程失败,并显示SIGPIPE;yes当管道关闭时会发生这种情况。要为您的 CI 屏蔽此错误,您需要使用类似的内容来屏蔽错误

(yes phrase ||:) | make installer

这将运行yes phrase,如果失败,则运行:并以代码 0 退出。这足够安全,因为yes除了无法写入之外,没有太多原因导致失败。

要调试此类管道问题,最好的方法是查看PIPESTATUS

yes phrase | make installer || echo "${PIPESTATUS[@]}"

这将显示失败时管道所有部分的退出代码。然后可以适当地处理那些因退出代码 141 而失败的情况。特定错误代码的通用处理模式是

(command; ec=$?; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi)

(谢谢豪克拉格);这会运行command,如果成功则以代码 0 退出command,或者如果以代码 141 退出。其他退出代码按原样反映。

答案2

有没有办法抑制管道错误,并只从中获取返回码make installer

如果您不想在子 shell 中运行整个命令管道(例如您希望能够设置变量),则可以使用已发布的其他优秀答案的替代解决方案:

yes phrase | make installer || { ec=$?; [ $ec -eq 141 ] && true || (exit $ec); }

# generic syntax:
cmd1 | cmd2 | cmd3 || { ec=$?; [ $ec -eq 141 ] && true || (exit $ec); }

这使用exit子 shell 中的命令来完整保留命令管道中的原始退出代码(如果它不是 141)。因此,如果set -e( set -o errexit) 与 一起生效,它将达到预期效果set -o pipefail

我们可以使用一个函数来获得更简洁的代码,它允许在子 shell 技巧中使用 来return代替:exit

handle_pipefails() { 
    # ignore exit code 141 from simple command pipes
    # - use with: cmd1 | cmd2 || handle_pipefails $?
    (( $1 == 141 )) && return 0
    return $1
}

# then use it or test it as:
yes | head -n 1 || handle_pipefails $?
echo "ec=$?"

# then change the tested code from 141 to e.g. 999 in
# the function, and see that ec was in fact captured as
# 141

如果您想测试更复杂的管道中涉及的其他命令的退出代码,另一种方法是测试整个 PIPESTATUS:

handle_pipefails2() {
    # ignore exit code 141 from more complex command pipes
    # - use with: cmd1 | cmd2 | cmd3 || handle_pipefails2 "${PIPESTATUS[@]}"
    for x in "$@"; do
        (( $x == 141 )) || { (( $x > 0 )) && return $x; }
    done
    return 0
}

答案3

( yes phrase ; exit 0 ) | make installer

答案4

经过一番挖掘,我找到了这个答案:https://stackoverflow.com/questions/22464786/ignoring-bash-pipefail-for-error-code-141#comment60412687_33026977 基本上,使用:

set -euxo pipefail
yes phrase | make installer || (ec=$? ; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi)

SIGPIPE只是从返回代码中过滤掉。

相关内容