Single UNIX仕様で意味set -e
を記述するために使用される正確な言語は次のとおりです。
このオプションがオンの場合、単純なコマンドが「シェルエラーの結果」にリストされている理由のいずれかで失敗するか、終了ステータス値> 0を返し、[条件付きコマンドまたは否定コマンド]ではない場合、シェルは直ちに終了します。
そのようなコマンドがサブシェルで発生したときに何が起こるかについてはあいまいさがあります。実用的な観点から見ると、サブシェルでできることは、終了してゼロ以外のステータスを親シェルに返すことだけです。親シェルが順に終了するかどうかは、この非ゼロのステータスが親シェルで失敗する単純なコマンドに変換されるかどうかに依存します。
このような問題のあるケースの1つは、コマンド置換からのゼロ以外の戻りステータスです。このステータスは無視されるため、親シェルは終了しません。すでに発見した、考慮に終了ステータスを取るための方法は、単純な中でコマンド置換を使用することです割り当て:その後、割り当ての終了ステータスが割り当て(S)の最後のコマンド置換の終了ステータスです。
最後の置換のステータスのみが考慮されるため、単一のコマンド置換がある場合にのみ、これが意図したとおりに実行されることに注意してください。たとえば、次のコマンドは成功します(標準と私が見たすべての実装の両方):
a=$(false)$(echo foo)
注目すべき別のケースは、明示的なサブシェルです(somecommand)
。上記の解釈によると、サブシェルはゼロ以外のステータスを返す場合がありますが、これは親シェルの単純なコマンドではないため、親シェルは続行する必要があります。実際、私が知っているすべてのシェルは、この時点で親を返します。これは(cd /some/dir && somecommand)
、現在のディレクトリの変更などの操作をローカルで保持するために括弧を使用する場合など、多くの場合に役立ちますがset -e
、サブシェルでオフになっている場合、またはサブシェルがゼロ以外のステータスを返す場合、仕様に違反します!
真のコマンドで使用するなど、終了しません。たとえば、次の例では、ash、bash、pdksh、ksh93、およびzshはすべて表示さfoo
れずに終了します。
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
まだset -e
有効な間、単純なコマンドは失敗しませんでした!
3番目の問題は、重要なパイプラインの要素です。実際には、すべてのシェルは、最後以外のパイプラインの要素の障害を無視し、最後のパイプライン要素に関する次の2つの動作のいずれかを示します。
- 親シェルでパイプラインの最後の要素を実行するATT kshおよびzshは、通常どおりビジネスを行います。パイプラインの最後の要素で単純なコマンドが失敗した場合、そのコマンドを実行するシェルはたまたま親シェルであり、終了します。
- 他のシェルは、パイプラインの最後の要素がゼロ以外のステータスを返す場合に終了することで動作を近似します。
前と同様に、set -e
パイプラインの最後の要素で無効にするか否定を使用すると、シェルを終了しないようにゼロ以外のステータスを返します。ATT kshおよびzsh以外のシェルは終了します。
Bashのpipefail
オプションを使用するとset -e
、その要素のいずれかがゼロ以外のステータスを返した場合、パイプラインがすぐに終了します。
さらに複雑なset -e
ことに、POSIXモード(set -o posix
またはPOSIXLY_CORRECT
bashの開始時に環境内にある)でない限り、bashはサブシェルでオフになることに注意してください。
これはすべて、POSIX仕様が残念ながら-e
オプションの指定で不十分な仕事をしていることを示しています。幸いなことに、既存のシェルの動作はほとんど一貫しています。