DESIGN / CODE / INFRA / AI

解説

途中で失敗したのに成功になる?Bashのパイプラインとpipefailを知ろう

false | catの終了状態をBashで比較。pipefailが変える値、複数の失敗での扱い、処理を自動停止する設定との違いを確認する。

途中で失敗したのに成功になる?Bashのパイプラインとpipefailを知ろう
保存リストを見る →
この記事の道しるべ 5項目

二つのコマンドを|でつないだら、前のコマンドが失敗したのに成功として扱われた。Bashのパイプラインは、初期状態では最後のコマンドの終了状態を使うからだよ。

今回はファイルを書き換えず、falseとcatを使って小さく確かめる。Bashが必要な例で、どのshでも同じ設定が使えるという説明ではないよ。

まず、最後のコマンドの値を見る

bash --noprofile --norc -c 'false | cat'
printf 'exit=%s\n' "$?"

falseは失敗を表す終了状態1を返し、catは入力を読み終えて成功の0を返す。この例で表示されるのはexit=0。前半の失敗を見ていないことになるね。

|は標準出力を次へ渡す仕組みで、前の処理が成功したことまで自動的に保証するわけではない。何も表示されなかったことも、成功の証拠にはならないよ。

pipefailを有効にすると変わる

bash --noprofile --norc -o pipefail -c 'false | cat'
printf 'exit=%s\n' "$?"

今度はexit=1になる。pipefailが有効だと、パイプラインに失敗したコマンドがある場合、その情報が終了状態へ反映される。

設定は呼び出した子のBashへ渡している。この練習のために、普段使っているシェルの設定ファイルを編集する必要はないよ。

複数が失敗したら、右側の失敗の値

bash --noprofile --norc -o pipefail -c '(exit 3) | (exit 7)'
printf 'exit=%s\n' "$?"

結果はexit=7。pipefailの値は、最後、つまり右側で最も後ろにある非0の終了状態になる。「最初に失敗した処理の番号」と覚えないようにしよう。

すべて成功なら0になる。どの処理が失敗したかを詳しく調べたい場合は、各コマンドの状態やログを別に確認する必要があるね。

失敗の値と、自動的な停止は別

pipefailはパイプラインの終了状態を変える設定。それだけで、後ろに書いたすべての処理が自動的に中止されるわけではないよ。

スクリプトでどう対処するかは、ifで成功・失敗を分けるなど、必要な制御を設計する。set -eにも適用条件や例外があるため、「失敗したら必ず止まる」一つの万能な指定として扱わないようにしよう。

$?は、次のコマンドで変わる

終了状態を見る$?は、直前の処理の値。確認前に別のコマンドを動かすと、その処理の終了状態へ変わる。

上の例でもprintfが終わったあとの$?は、printfの値になる。後から保存しておきたい場合は、対象の直後に変数へ取るなど、見る時点を揃える必要があるよ。

編集部ではBashで、初期状態の0、pipefail有効時の1、二つの失敗の7を確認した。終了コードの基本、パイプとリダイレクトも合わせて読むと整理しやすいよ。

参考・出典

公開と変更の記録
  • :初回公開

日時は日本時間です。記載のある変更は、確認できた編集記録に基づきます。更新日の違いだけから変更内容を推測して載せることはありません。

更新・訂正の方針 →

みんなのコメント 0

感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。

最初のコメントをどうぞ。