
在 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[@]}
.
該行為是預期的。使用 subshell 而不是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