角かっこ()の後にORリスト| |があるサブシェルでset -eが機能しないのはなぜですか?

角かっこ()の後にORリスト| |があるサブシェルでset -eが機能しないのはなぜですか?

最近、次のスクリプトが見つかりました。

( set -e ; do-stuff; do-more-stuff; ) || echo failed

私が見るのは大丈夫に見えますが、うまくいきません!set -e追加時には適用されません||。それがなければうまくいきます。

$ ( set -e; false; echo passed; ); echo $?
1

||ただし、: を追加するとset -e無視されます。

$ ( set -e; false; echo passed; ) || echo failed
passed

実際のスタンドアロンシェルを使用すると、期待どおりに機能します。

$ sh -c 'set -e; false; echo passed;' || echo failed
failed

私はこれをいくつかの異なるシェル(bash、dash、ksh93)で試してみましたが、すべて同じように動作するので、バグではありません。誰かがこれを説明できますか?

ベストアンサー1

~によるとこのスレッドset -e、これはサブシェルで ""を使用するためのPOSIX指定の動作です。

(私も驚きました。)

まず、行動は次のとおりです。

-e!で始まるパイプに対してwhile、Until、if、またはelifの予約語の後にある複合リストを実行するときは、この設定は無視する必要があります。予約語またはAND-ORリストの最後の項目を除くすべてのコマンド。

2番目の記事では、次のように言います。

とにかく(サブシェルコード)で-eを設定すると、周囲のコンテキストとは無関係に動作しませんか?

習慣。 POSIXの説明は、周囲のコンテキストがサブシェルでset -eが無視されるかどうかに影響を与えることを明らかにします。

エリック・ブレイクが書いた4番目の記事では、より多くの内容を見ることができます。

ポイント3はいいえ無視されたコンテキストをオーバーライドするようにサブシェルに要求しますset -e。つまり、-e無視されたコンテキストにある場合何もない-eサブシェルでもまた従順を得るためにできることがあります。

$ bash -c 'set -e; if (set -e; false; echo hi); then :; fi; echo $?' 
hi 
0 

これを2回呼び出しても(親シェルとサブシェルで)、サブシェルが無視されたコンテキスト(ifステートメントの条件)set -eに存在するため、サブシェルでそれを再度有効にするために何もできません。-e-e

この行動は本当に素晴らしいです。これは直観に反します。再度有効にすると効果があると予想される可能性set -eがあり、周囲の環境が優先されることはなく、POSIX標準の表現ではこれを明確に示していません。コマンドが失敗した場合に読んだ場合、このルールは適用されません。周辺コンテキストにのみ適用されますが、全体的に適用されます。

おすすめ記事