bashのset -eが効かない5つの場面と対処法

はじめに:set -e を書いたのに止まらない

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 で確かめたものです。

bashのset -eが効かない5つの場面と対処法

はじめに:set -e を書いたのに止まらない

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 で確かめたものです。