web-dev-qa-db-ja.com

&&式の左側で呼び出される関数内のエラーで失敗する

私はいくつかの関数を備えたBashスクリプトに取り組んでいます。コマンドがゼロ以外の終了コードを返した場合、そのコードが明示的に処理されているコンテキスト(if条件または||代替の前など)を除いて、スクリプトを終了したいと思います。これを簡単にするために、スクリプトからサブシェルの使用をすべて削除し、見つかったすべてのエラー処理オプションを有効にしました。

残念ながら、エラーが抑制されている一般的なパターンがまだ発生しています。

関数内のコマンドがゼロ以外の終了コードを返すが、関数内の最後のコマンドではなく、関数が&&式の最初の部分として呼び出されている場合、終了コードは無視されます。

#!/bin/bash

trap 'echo "Error occurred." && exit' ERR
set -o errexit;  # -e
set -o errtrace; # -E
set -o pipefail;

first-step-fails() {
    # These should be redundant, but are repeated to be certain and clear.
    trap 'echo "Error occurred." && exit' ERR
    set -o errexit;  # -e
    set -o errtrace; # -E
    set -o pipefail;

    false; # exit code 1 (failure)
    true;  # exit code 0 (success)

    echo "A is executed.";
}

first-step-fails && echo "B is executed.";
A is executed.
B is executed.

echoの呼び出し後にfalsesが実行されるとは思っていませんでした。


関数が&&式で呼び出されない場合、エラーはトラップされます。

#!/bin/bash

trap 'echo "Error occurred." && exit' ERR
set -o errexit;  # -e
set -o errtrace; # -E
set -o pipefail;

first-step-fails() {
    # These should be redundant, but are repeated to be certain and clear.
    trap 'echo "Error occurred." && exit' ERR
    set -o errexit;  # -e
    set -o errtrace; # -E
    set -o pipefail;

    false; # exit code 1 (failure)
    true;  # exit code 0 (success)
    echo "A is executed.";
}

first-step-fails ### && echo "B is executed.";
Error occurred.

失敗が関数の最後のステップである場合、エラーはトラップされませんが、もちろん関数はゼロ以外の終了ステータスを渡し、外側のechoを抑制します。

#!/bin/bash

trap 'echo "Error occurred." && exit' ERR
set -o errexit;  # -e
set -o errtrace; # -E
set -o pipefail;

first-step-fails() {
    # These should be redundant, but are repeated to be certain and clear.
    trap 'echo "Error occurred." && exit' ERR
    set -o errexit;  # -e
    set -o errtrace; # -E
    set -o pipefail;

    false; # exit code 1 (failure)
    ### true;  # exit code 0 (success)
    ### echo "A is executed.";
}

first-step-fails && echo "B is executed.";
(no output)

&&式の左側で呼び出される関数内で呼び出されたコマンドからゼロ以外の終了ステータスが返された場合、bashスクリプトを終了させるにはどうすればよいですか?

私はmacOS(組み込みGNU Bash 4.4.23)でテストしていますが、Alpine Linux(パッケージ化されたGNU Bash4.4)でも機能するソリューションが必要です。 19)。

2
Jeremy Banks

このset -eの説明のように私には見えます:

失敗したコマンドが... &&または||で実行されたコマンドの一部である場合、シェルは終了しません。リスト

は、「&&リストの一部」を自由に解釈して、関数呼び出し内のすべてのコマンドを含めます。その結果、関数内の単純な失敗したコマンドでさえ、&&チェーンで呼び出されたときにERRトラップをトリガーしません。

関数のコマンドの1つが失敗したときにその実行を停止する必要がある場合、1つの可能性は、それらすべてをブロック内でチェーンすることです。

first-step-fails() {
    { 
      true &&
      false &&
      true;
    } || exit 1

    echo "A is executed.";
}

first-step-fails && echo "B is executed.";

...結果は出力されず、戻りコードは1になります。

1
Jeff Schaller