web-dev-qa-db-ja.com

散発的な141シェルスクリプトエラーのデバッグ

CI(Gitlab、Alpine Linuxを実行しているDockerコンテナー)でスクリプトを実行しているときに、「SIGPIPE」を示しているように見えるシグナル141で散発的なエラーが発生します。しかし、どのステップが失敗しているのか、それをデバッグするために何をすべきかがわかりません。

  #!/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 ))

散発的なエラーは、最後から2番目の行のパイプ内で発生しているようです。取得したログは次のとおりです。

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

どの行が実際に失敗したかを把握するために、これをデバッグするにはどうすればよいですか? RELEASE変数が正常に入力されたように見えますが、それでもどういうわけか爆発しましたか?

2
kos

head -1がジョブを実行した後、sort -nrがすべてのデータをパイプに書き込むことができたかどうかに関係なく終了します。 sortに書き込むものがあり、headがなくなった場合、sortSIGPIPEを取得します。

何も爆発しません。これがパイプの仕組みです。パイプで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[@]}を調べます。


動作は予想されます。パイプのこの部分の終了ステータスを操作するには、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
2