終了ステータスがゼロ以外のコマンドが「&&または||リスト」の一部であるにもかかわらず、ERR信号を送信するのはなぜですか?

終了ステータスがゼロ以外のコマンドが「&&または||リスト」の一部であるにもかかわらず、ERR信号を送信するのはなぜですか?

man bash使用するには、次の文書を含めてくださいtrap

trap [-lp] [[arg] sigspec ...]
    The ERR trap is not executed if the failed command is part of the command list
    immediately following a while or until keyword, part of the test in an if statement,
    part of a && or || list, or if the command's return value is being inverted via !.
    These are the same conditions obeyed by the errexit option.

私は理解「&& または || の部分リスト」これは、コマンドがこれらの制御演算子を含むリストの一部である場合、終了状態がゼロでない場合でも同様です。ERR信号を送信しないでください。(または使用されている場合はスクリプトを終了しますset -o errexit)。

しかし、ここに矛盾するように見えるテストスクリプトがあります。

#!/usr/bin/env bash

_trap_err() {
    local status=$? sig=$1 line=$2;
    echo "Exit status ${status} on line ${line}: \`${BASH_COMMAND}\`";
}
trap '_trap_err ERR $LINENO' ERR;

function control_operators() {
    # The next line will send an ERR signal.
    [[ 1 -eq 2 ]] && echo Hello;
}

control_operators;

# The next line will not send an ERR signal.
[[ 1 -eq 2 ]] && echo Hello;

echo Done;

出力は次のとおりです

❯ test.sh
Exit status 1 on line 14: `[[ 1 -eq 2 ]]`
Done

[[ 1 -eq 2 ]]&& または || の一部として「熱くなっている」からです。ERR信号はトリガーしてはいけません。

予想される出力は次のとおりです。

❯ test.sh
Done

また、ちょうどリスト[[ 1 -eq 2 ]] && echo Hello ~へこのcontrol_operators関数は ERR 信号を送信しますが、[[ 1 -eq 2 ]] && echo Hello 外部この機能はそうではありません。

このスクリプト環境で設定されたオプションは次のとおりです(出力set -o | grep 'on$')。

braceexpand     on
hashall         on
interactive-comments    on
xtrace          on

質問:

  1. この動作は正常ですか、またはリストで制御演算子を誤って使用していますか(または文書を誤って解釈していますか?)。
  2. コマンドが条件式の一部であり、実際にエラー条件を構成していない場合、ERRトラップトリガを防ぐ最善の方法は何ですか?
    • || true1つのオプションは、各リストの最後に追加された制御演算子を使用することです。
    • if [[ 1 -eq 2 ]]; then echo Hello; fi別のオプションは代わりに略語を使用することです。これは(によると)「ifステートメントのテストの一部」なので、&&ERR信号は送信されません。man bash&& または ||" . リスト" いいえ。
  3. [[ 1 -eq 2 ]]ERR信号が関数内でのみ送信されるのはなぜですか?

修正する:@Kusalanandaの最初の答えは上記の例では正しいです(関数に追加するとERR信号がreturn 0防止されます)。control_operatorsただし、以下は、「&&または||ルールを含むリストの一部」に違反しているように見える別の例です。

#!/usr/bin/env bash
_trap_err() {
    local status=$? sig=$1 line=$2;
    echo "Exit status ${status} on line ${line}: \`${BASH_COMMAND}\`";
}
trap '_trap_err ERR $LINENO' ERR;
true && false;
echo Done;

このスクリプトの出力は次のとおりです。

❯ test.sh
Exit status 1 on line 7: `false`
Done

このtrue && false行は、コマンドがfalse「&&または||リストの一部」であるため、ERR信号を送信しないでください。それでは、なぜこれを行うのですか?

アップデート2:私はbash 3.2を使ってmacOSで作業しています。これがまさにman bash上記のスニペットで言及されていない理由です。「最後の&&または||を除くコマンド」

ベストアンサー1

ERR呼び出しの戻り状態がゼロではないため、スクリプトはcontrol_operatorsトラップをトリガーしています。関数テストはそうではありません。まっすぐ罠を発動させてください。

トラップ出力は、関数が呼び出された行になる行番号を印刷することによってこれを示します。

また、トラップ出力は、ゼロ以外の終了状態がどこから来るかを示し、これは関数テストの結果です。テストは関数で最後に実行されるため、関数の終了状態を設定します。

関数呼び出しが AND または OR リストの一部ではないため、トラップがトリガーされます。


更新された質問に対してERRゼロ以外の終了ステータスを返すコマンドが次の場合、トラップが引き続き呼び出されることを忘れました。最後リストの AND または OR コマンドです。

bashマニュアルでは、私は以下を強調します。

ERR失敗したコマンドが、whileまたはuntilキーワードの直後にあるコマンドのリストの一部、ステートメントのテスト部分、またはリストからif実行されたコマンドの一部である場合、トラップは実行されません。&&||&&最後または次のコマンドを除いて||、最後のコマンドを除くパイプラインのすべてのコマンド、またはリバースコマンドの戻り値を使用する場合 !

あなたの例は、ANDリストの最後true && falseなので、トラップをトリガーします。falseERR

それを表現する別の方法は次のとおりですERR終了状態に決定リストまたはパイプに対してゼロ以外の終了状態を返します(!またはこのコマンドで使用されている場合は0)。

おすすめ記事