$ set -o pipefail
$ set +o pipefail
というコマンドがおまじない的に使われているのをよく見ます。
今回は、担当することになったプロダクトのCIにこのコマンドが含まれていたこともあり、処理の詳細を調べました。
その覚書もかねて、解説記事を残します。
TL;DR
set ±o pipefail
は、パイプラインを用いるコマンドの終了ステータスに影響する。set -o pipefail
を使うと、パイプライン内のいずれかのコマンドが失敗した場合、その失敗したコマンドの終了ステータスがパイプライン全体の終了ステータスとなる。set +o pipefail
を使うと、パイプライン末尾の終了ステータスがパイプライン全体の終了ステータスとなる。(デフォルトなので無くても良い)
解説
通常、bashはコマンドが実行されるたび、終了ステータス*1を更新します。
例として、簡単なシェルスクリプトを実行してみましょう。
#!/bin/sh true echo $? false echo $? # bashの終了ステータスは $? で確認できる
このスクリプトの実行結果は次の通りです。
0 1
一般に、コマンドが正常終了したら 0 、コマンドが異常終了したら 0 以外が終了ステータスが記録されます。
終了ステータスの範囲は 0~255 です。
よく使われるのは、次の予約済みのステータス*2でしょうか。
終了ステータス | 意味 |
---|---|
1 | 一般的なエラー |
2 | ビルトインコマンドの誤用 |
126 | コマンドを実行できなかった (例:実行権限がない) |
127 | コマンドが見つからなかった |
128 | exit に不正な値を渡した(例:浮動小数点) |
255 | exit に範囲外の終了ステータスを渡した |
では、パイプラインを使う際の終了ステータスはどう更新されるでしょう。
#!/bin/sh true | true echo "$?" false | true echo "$?" true | false echo "$?"
0 0 1
終了ステータスには、パイプライン末尾のコマンドの終了ステータスが渡されます。
しかし2番目のパイプラインから分かるように、先頭のコマンドがエラーなのに、終了ステータスが正常終了を表す 0 になっては困るときが多いでしょう。
そんな時に、set -o pipefail
が役立ちます。
では、 set -o pipefail
を冒頭で宣言して、同様のスクリプトを実行してみましょう。
#!/bin/sh set -o pipefail true | true echo "$?" false | true echo "$?" true | false echo "$?"
0 1 1
上記の通り、実行を失敗したコマンドの終了ステータスが変わっていることがわかります。
これで、パイプライン内のコマンドに対してもエラー制御しやすくなりました。
ちなみに、デフォルトは set +o pipefail
です。
デフォルトですが、可読性のためにあえて明示的に set +o pipefail
を宣言してもいいですし、スクリプトの途中で ±
を切り替えるために使っても良いかもしれませんね。
少しだけDeep Dive
set -o pipefail
を使うと、パイプライン内で失敗したコマンドの中でもより後者の終了ステータスが反映されるようです。
#!/bin/sh set -o pipefail false | not-exist-command | true echo "$?" not-exist-command | false | true echo "$?"
127 1
また、set -o pipefail
の他にも、set -e -u -x pipefail
と色々とバリエーションがあります。
気になる方は次の参考リンクなどから調べてみてください。
*1:直前に実行されたコマンドの状態を表す数値(英: exit status)