_errexit継承が機能しない場合はいつですか?

_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ですechoecho書き込めない場合を除く)。したがって、スクリプトがset -e有効になってもここで停止しません。同様に、3番目のスクリプトではf $(…)ステータスは0ですf

関連情報