set -euo pipefail を先頭に書いておけば、途中で失敗したら止まる。そう思って書いたスクリプトが、失敗の後も平気で進んでいたことはありませんか?
結論から書くと、set -e には 効かない文脈 があり、しかもその中で呼んだ関数の中身まで丸ごと無効になります。代表的なのは次の 5 つです。
| 場面 | 何が起きるか | 直し方 |
|---|---|---|
f || rc=1 のように呼んだ関数 | 関数の中のどの失敗でも止まらない | 関数の中で || return 1 を書く |
echo "$(cmd)" | cmd が失敗しても空文字で進む | いったん x=$(cmd) で受ける |
local x=$(cmd) | local の成功 (0) で失敗が消える | 宣言と代入を分ける |
最後が A && B の関数 | A が偽だと関数が 1 を返し、呼び出し元で止まる | if で書くか return 0 |
x=$(jq ...) | 壊れた JSON でも先頭の値が入る | 終了ステータスを必ず見る |
以下の実行結果は、どれも GNU bash 5.3.15 で確かめたものです。
set -euo pipefail を先頭に書いておけば、途中で失敗したら止まる。そう思って書いたスクリプトが、失敗の後も平気で進んでいたことはありませんか?
結論から書くと、set -e には 効かない文脈 があり、しかもその中で呼んだ関数の中身まで丸ごと無効になります。代表的なのは次の 5 つです。
| 場面 | 何が起きるか | 直し方 |
|---|---|---|
f || rc=1 のように呼んだ関数 | 関数の中のどの失敗でも止まらない | 関数の中で || return 1 を書く |
echo "$(cmd)" | cmd が失敗しても空文字で進む | いったん x=$(cmd) で受ける |
local x=$(cmd) | local の成功 (0) で失敗が消える | 宣言と代入を分ける |
最後が A && B の関数 | A が偽だと関数が 1 を返し、呼び出し元で止まる | if で書くか return 0 |
x=$(jq ...) | 壊れた JSON でも先頭の値が入る | 終了ステータスを必ず見る |
以下の実行結果は、どれも GNU bash 5.3.15 で確かめたものです。