5

この質問trapのために関数内で使用して遊んでいましたが、この二次的な質問を思いつきました。次のコードがあるとします。

d() {
    trap 'return' ERR
    false
    echo hi
}

を実行dするtrapと、シェルは「hi」を出力せずに関数から戻ります。ここまでは順調ですね。しかし、もう一度実行すると、シェルから次のメッセージが表示されます。

-bash: return: 関数またはソース スクリプトからのみ「戻る」ことができます

最初は、これは ERR sig が 2 回発生したことを意味すると想定しました。1 回目falseはゼロ以外の終了ステータスが返されたとき (関数内)、もう 1 回は関数自体がゼロ以外の終了ステータスで返されたとき (関数外) です。しかし、その仮説はこのテストに耐えられません。

e() {
    trap 'echo +;return' ERR
    false
    echo hi
}

上記を実行すると、どれだけ頻繁に実行しても、関数またはソーススクリプトからのみreturn警告が表示されなくなります。シェルが引数内の単純なコマンドとは異なる複合コマンドを扱うのはなぜtrapですか?

私の目標は、関数を終了させたコマンドの実際の終了ステータスを維持することでしたが、上記の動作の原因が何であれ、終了ステータスのキャプチャも複雑になると思います。

f() {
    trap '
        local s=$?
        echo $s
        return $s' ERR
    false
    echo hi
}

bash> f; echo $?
1
0

ワット?$sここで が 2 つの異なる値に展開される理由と、同じ原因であることが判明した場合は、上記の と の区別をreturn誰かが説明できますかecho +; return?

4

1 に答える 1

3

あなたの最初の結論は正しかった: ERR sig が 2 回発生していた.

「d」の最初の実行中に、トラップをグローバルに定義します。これは次のコマンドに影響します (d の現在の呼び出しは影響を受けません)。'd' の 2 回目の実行中にトラップを再度定義すると (あまり役に立ちません)、'false' の呼び出しが失敗するため、トラップによって定義されたハンドラーを実行します。次に、'd' も失敗した親シェルに戻るので、トラップを再度実行します。

ただの発言。ERR は 'sigspec' 引数として指定できますが、ERR はシグナルではありません ;-) BASH のマニュアルから:

If a sigspec is ERR, the command arg is executed whenever a sim‐
ple command has a non-zero exit status, subject to the following
conditions. [...]
These are  the  same  conditions obeyed by the errexit option.

関数「e」を使用すると、ERR ハンドラは「echo」コマンドを実行して成功します。これが、'e' 関数が失敗しない理由です。この場合、ERR ハンドラーが 2 回呼び出されないのはそのためです。

「e; echo $?」「0」と読みます。

次に、「f」関数を試しました。私は同じ行動を観察しました(そして私は驚きました)。原因は、「$s」の不適切な拡張ではありません。値をハードコーディングしようとすると、「return」ステートメントに指定された引数が、トラップ ハンドラーによって実行されるときに無視されることに注意してください。

それが正常な動作なのか、BASH のバグなのかはわかりません... それとも、インタープリターで無限ループを回避するためのトリックかもしれません :-)

ところで、私の意見では、それはトラップの良い使い方ではありません。サブシェルを作成することで、トラップの副作用を回避できます。この場合、親シェルでエラーを回避し、内部関数の終了コードを保持します。

g() (
    trap 'return' ERR
    false
    echo hi
)
于 2014-05-24T20:29:28.143 に答える