

set -eだけでは止まらないパイプラインエラーとpipefailの必要性
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
シェルスクリプトの冒頭でよく見る set -euo pipefail。それぞれ何をしているか、なぜ3つセットで使われるかを整理する。
それぞれの意味
set -e:コマンドが非ゼロの終了ステータスを返した時点でスクリプトを即座に停止するset -u:未定義(未設定)の変数を参照した時点で停止するset -o pipefail:パイプライン中のどこかのコマンドが失敗したら、パイプライン全体を失敗扱いにするset +e:-の代わりに+を付けると無効化になる。有効化が-、無効化が+という、多くのCLIツールとは逆の挙動なので覚えておくといい
順序に意味はなく、set -eu でも set -ue でも同じ。
set -e だけでは止まらないケース
set -e は「非ゼロ終了で必ず止まる」わけではない。以下のケースでは止まらない。
1. if / while / until の条件式の中
if grep foo file.txt; then echo "found"figrep が1(マッチなし)を返してもスクリプトは止まらない。条件式として使われた場合は set -e の対象外。
2. && や || の右左にあるコマンド
some_command || trueこれは意図的に失敗を握りつぶす定番パターンだが、逆に言うと || や && の中にいるコマンドの失敗は set -e では拾われない。
3. パイプラインの最後以外のコマンド
false | grep fooパイプラインの終了ステータスは、デフォルトでは最後のコマンド(この場合 grep)のものしか見ない。false がパイプの前段で失敗していても、grep が成功していればパイプライン全体は成功扱いになり、set -e は反応しない。
pipefail で3番目のケースを拾う
set -euo pipefailfalse | grep foo # ここで停止するecho "ここには来ない"pipefail を有効にすると、パイプライン中のどれか一つでも失敗すればパイプライン全体が失敗扱いになる。これと -e が組み合わさって、パイプの途中の失敗も検知できるようになる。
注意点
set -o pipefailは bash / zsh の機能で、POSIX shには存在しない。シェバンが#!/bin/shだと動かない(dashなど一部シェルはエラーになる)ので、使うなら#!/bin/bashにするpipefailを有効にしても、意図的に失敗を無視したいパイプがあるなら個別に|| trueを付ける必要がある
実務での形
#!/bin/bashset -euo pipefail
# 一時的に緩めたい場合set +esome_command_that_may_failset -eこのセットが「シェルスクリプトの事故防止」としては現状のデファクトスタンダードになっている。