次のスクリプトを使用しています。
_#!/bin/bash -Eu
trap 'echo Hi' ERR
exit_failure() {
echo "Hello, World!"
return 1
}
sub_failure() {
res=$(exit_failure)
}
sub_failure
_
その結果、次のようになります。
_Hi
Hi
_
ただし、sub_failure()
を次のように変更すると次のようになります。
_sub_failure() {
local res=$(exit_failure)
}
_
出力がありません。 ERR
はもうトラップされていませんか?なぜ信号が隠されているのですか?ローカル変数を使用したい場合、どうすればERR
をトラップできますか? local res; res=$(exit_failure)
を実行できることはわかっていますが、なぜ両方を分離する必要があるのですか?
バグではありません。これは実際に定義された動作です。
_bash -Eux
_を使用すると、何が起こるかを確認できます。 (_-Eu
_あなたのシバンから+ _-x
_)
_+ trap 'echo Hi' ERR
+ sub_failure
++ exit_failure
++ echo 'Hello, World!'
++ return 1
+++ echo Hi
+ res='Hello, World!
Hi'
++ echo Hi
Hi
++ echo Hi
Hi
_
コマンド置換を行う場合、_-E
_スイッチのためにtrap
が継承されます。したがって、exit_failure()
関数の_return 1
_によってトリガーされる継承されたトラップからの「Hi」は、ret
に格納されている値の一部になります。 (これは、local
を使用してバリアントを実行する場合にも当てはまります)
さらに、_res=...
_式は_1
_(エラー)を返し、トラップをトリガーします(sub_failure()
関数内)。
_res=...
_は_1
_を返し、関数の結果は関数の最後のコマンドの結果であるため、sub_failure()
の結果も_1
_(エラー)であり、メインシェルで_sub_failure
_が実行された後、トラップが再度トリガーされます。したがって、2つの表示可能な「Hi」を取得します。1つは_res=....
_用、もう1つは_sub_failure
_用、非表示の「Hi」は_$res
_に格納されます。
local
バリアントの場合:
_+ trap 'echo Hi' ERR
+ sub_failure
++ exit_failure
++ echo 'Hello, World!'
++ return 1
+++ echo Hi
+ local 'res=Hello, World!
Hi'
_
定義上、local
は、関数で使用されると常に_0
_を返します。非表示の「Hi」を_local res=...
_に格納したまま、_0
_を_$res
_(成功)と評価します。また、_res=..
_は_0
_に評価されるため、_sub_failure
_も_0
_を返します。したがって、今回は「隠された」失敗が1回、成功が2回発生します。
このスレッドが静かな古いものであっても、これが役立つことを願っています;)
また、_local res=...
_を分割する理由も明確にする必要があります
_local res
res=....
_
最初のバリアントの動作を復元しますか? ;)