BashでSIGPIPEを抑制する方法は?

BashでSIGPIPEを抑制する方法は?

次のコードを実行しようとしています。

set -euxo pipefail
yes phrase | make installer

Makefile標準入力を使用してインストーラphraseファイルを作成します。ただし、このコマンドはCIビルドを中断するエラーコード141で終わります。この例は、次のように単純化できます。

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

サブシェルで完全なコマンドパイプラインを実行したくない場合(たとえば、変数を設定できるようにしたい場合など)、公開されている他の優れた答えに対する代替ソリューションを使用できます。

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

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

これはexitサブシェルのコマンドを使用して、コマンドパイプラインに元の終了コードを残します(141以外の場合)。したがって、 set -eset -o errexit)を一緒に操作すると、目的の効果が得られますset -o pipefail

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

より複雑なパイプラインに関連する他のコマンドの終了コードをテストする場合は、もう1つのアプローチは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戻りコードでフィルタリングされました。

関連情報