inherit_errexit が機能しないのはどのような場合ですか?

inherit_errexit が機能しないのはどのような場合ですか?
#!/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→ リダイレクト失敗のためステータス1
  • false </dev/null→ ステータス1からfalse
  • a=b→ 故障する可能性のある部品がないため、ステータス0
  • a=$(exit 0) b=$(exit 1) c=$(exit 2)→ 最後のコマンド置換からのステータス2
  • a=$(exit 2) b=$(exit 1) c=$(exit 0)→ 最後のコマンド置換からのステータス0
  • true $(exit 0) $(exit 1) $(exit 2)→ ステータス0からtrue

繰り返しになりますが、すべてのケースにおいて、set -eコマンドのステータスがゼロ以外の場合のみスクリプトが停止します。埋め込まれたコマンドのステータスは直接関係ありません。

したがって、 2 番目のスクリプトでは、コマンド置換内で何が起こってもecho -n $(…)、 のステータスは から 0 になりますecho(書き込みに失敗した場合を除くecho)。したがって、 がアクティブであっても、スクリプトはここで停止しませんset -e。同様に、3 番目のスクリプトでは、f $(…)のステータスは から 0 になりますf

関連情報