#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
a=$(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
f() { :; }
f $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
他のケースもありますか?またはいくつかの一般化?
答え1
TL、DR:この機能を利用するには、set -e
コマンド置換結果を変数に直接割り当てます(オプションで、周囲に追加の文字列を追加します)。複数のコマンド置換を組み合わせたり、コマンドパラメータにコマンド置換を使用したりしないでください。
問題はinherit_errexit
うまくいきません。問題はset -e
(これはbashに限定されていません。他のshと同様のシェルにも同じ問題があります)制限です。
デモ:2番目の例のバリエーションを実行します。
$ cat b2.sh
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file; echo >&2 after cat)
echo survived
$ ./b2.sh
cat: no-such-file: No such file or directory
survived
echo >&2 after cat
実行がないことに注意してください。閉じるとこれが起こりますinherit_errexit
。
問題は、set -e
エラーが発生した場合、単純な場合にのみ実行が停止することです。コマンド置換が失敗状態を返す場合、置換を含む単純コマンドの実行は停止しません。せいぜい簡単なコマンドの戻り状態を設定することで、スクリプトの実行を停止できます。 「単純なコマンド」は、割り当て、リダイレクト、オプションの実行可能なコマンド名、および引数で構成されます。リダイレクトが失敗した場合、戻り状態は 1 です。それ以外のコマンド名がある場合、単純コマンドの戻り状態は実行可能コマンドの戻り状態です。それ以外の場合、戻り状態は最後のコマンド置換の戻り状態、または存在しない場合は0です。以下は、単純なコマンドのいくつかの例です。
true </no/such/file
→リダイレクト失敗のため状態1false </dev/null
→状態1からfalse
a=b
→失敗する可能性のあるコンポーネントがないため、状態0a=$(exit 0) b=$(exit 1) c=$(exit 2)
→最後のコマンド置換状態2a=$(exit 2) b=$(exit 1) c=$(exit 0)
→最後のコマンド交換後の状態0true $(exit 0) $(exit 1) $(exit 2)
→ステータス0からtrue
繰り返しますが、すべての場合にset -e
コマンドの状態がゼロでない場合にのみスクリプトが停止します。含まれているコマンドの状態は直接関係ありません。
したがって、2番目のスクリプトでは、コマンド置換で何が起こってもecho -n $(…)
状態は0ですecho
(echo
書き込めない場合を除く)。したがって、スクリプトがset -e
有効になってもここで停止しません。同様に、3番目のスクリプトではf $(…)
ステータスは0ですf
。