
#!/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
。