调试偶发的 141 shell 脚本错误

调试偶发的 141 shell 脚本错误

在 CI(Gitlab、运行 Alpine Linux 的 Docker 容器)上运行脚本时,我遇到了零星故障,信号 141 似乎表示“SIGPIPE”。但我不明白哪一步失败了或者如何调试它。

  #!/usr/bin/env bash

  set -euxo pipefail
  set -a

  # ...
  git fetch --tags 

  RELEASE=$(git tag | grep -E "${BUILD_ENV}-release-(\d+)" | cut -d"-" -f3 | sort -nr | head -1)
  RELEASE=$(( RELEASE + 1 ))

偶发错误似乎发生在倒数第二行管道内,我得到的日志是:

++ git tag
++ cut -d- -f3
++ sort -nr
++ grep -E 'prod-release-(\d+)'
++ head -1
+ RELEASE=323
ERROR: Job failed: exit code 141

我将如何调试它以找出哪一行实际上失败了?看起来它成功填充了 RELEASE 变量,但不知何故仍然爆炸了?

答案1

head -1完成其工作后,无论是否sort -nr设法将所有数据写入管道,它都会退出。如果sort有更多的东西要写并且head没有更多的东西,那么sort就会得到SIGPIPE

没有什么会爆炸。这就是管道的工作原理。当您head在管道中使用时,如果前面的命令由于SIGPIPE.


简单测试,yes而不是sort

$ set -o pipefail
$ yes | head -n 1
y
$ printf '%s\n' "$?" "${PIPESTATUS[@]}"
141             <- overall exit status
141             <- from `yes'
0               <- from `head'

退出状态是141,它来自yes

这展示了如何调试:通过检查${PIPESTATUS[@]}.


该行为是预期的。使用 subshel​​l 而不是yes(或者在您的情况下而不是sort)来操纵管道这部分的退出状态。例子:

$ set -o pipefail
$ (yes; true) | head -n 1
y
$ printf '%s\n' "$?" "${PIPESTATUS[@]}"
0
0
0

仅抑制更复杂的逻辑141

(yes; e="$?"; [ "$e" -eq 141 ] && exit 0; exit "$e") | head -n 1

相关内容