この問題に対処するには、さらにいくつかの方法があります。要件の 1 つは、いくつかのシェル コマンドを含むシェル スクリプト/関数を実行し、スクリプトが正常に実行されたかどうかを確認し、失敗した場合はエラーをスローすることであると仮定します。
通常、シェル コマンドは返された終了コードに依存して、予期しないイベントが原因で成功したか失敗したかをシェルに知らせます。
あなたがやりたいことは、これらの 2 つのカテゴリに分類されます
- エラーで終了
- エラー時の終了とクリーンアップ
どちらを実行するかに応じて、使用できるシェル オプションがあります。最初のケースでは、シェルは set -e
でオプションを提供します 2 番目に trap
を実行できます EXIT
に
exit
を使うべきですか 私のスクリプト/関数で?
exit
の使用 一般的に可読性を向上させます 特定のルーチンでは、答えがわかったらすぐに呼び出し元のルーチンに戻りたいと思うでしょう。エラーを検出した後、それ以上のクリーンアップを必要としないようにルーチンが定義されている場合、すぐに終了しないということは、より多くのコードを記述する必要があることを意味します。
したがって、スクリプトの終了をクリーンにするためにスクリプトでクリーンアップ アクションを実行する必要がある場合は、しないことをお勧めします。 exit
を使用するには .
set -e
を使うべきですか 終了時のエラー?
<ブロック引用> いいえ!
set -e
シェルに「自動エラー検出」を追加する試みでした。その目標は、エラーが発生するたびにシェルを中止させることでしたが、たとえば
-
if テストの一部であるコマンドは影響を受けません。この例では、
test
で壊れると予想される場合 存在しないディレクトリをチェックしますが、そうではありません。else 条件に進みますset -e f() { test -d nosuchdir && echo no dir; } f echo survived
-
最後のパイプライン以外のパイプライン内のコマンドは影響を受けません。以下の例では、最後に実行された (一番右の) コマンドの終了コードが考慮されるため (
cat
)そして成功しました。これはset -o pipefail
で設定することで回避できます オプションですが、それでも注意事項があります。set -e somecommand that fails | cat - echo survived
推奨 - trap
終了時
set -e
を使用する代わりに、やみくもに終了するのではなく、エラーを処理できるようにしたい場合の判断です。 、 trap
を使用 ERR
で 疑似信号。
ERR
トラップは、シェル自体がゼロ以外のエラー コードで終了したときにコードを実行するのではなく、条件の一部ではないそのシェルによって実行されたコマンド (if cmd
など) のときに実行されます。 、または cmd ||
) ゼロ以外の終了ステータスで終了します。
一般的には、トラップ ハンドラーを定義して、どの行と何が原因で終了したかに関する追加のデバッグ情報を提供します。 ERR
の原因となった最後のコマンドの終了コードを覚えておいてください 信号はこの時点でまだ利用可能です.
cleanup() {
exitcode=$?
printf 'error condition hit\n' 1>&2
printf 'exit code returned: %s\n' "$exitcode"
printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
そして、失敗しているスクリプトの上で以下のようにこのハンドラーを使用するだけです
trap cleanup ERR
これを false
を含む単純なスクリプトにまとめる 15行目で、取得する情報
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
trap
エラーに関係なく、シグナル EXIT
で、シェルの完了時 (シェル スクリプトの終了時など) にクリーンアップを実行するオプションも提供します。 .同時に複数のシグナルをトラップすることもできます。トラップするサポートされているシグナルのリストは、trap.1p - Linux マニュアル ページにあります
もう 1 つ注目すべき点は、サブシェルを扱っている場合、提供されているメソッドはどれも機能しないことを理解することです。その場合、独自のエラー処理を追加する必要があるかもしれません。
-
set -e
のサブシェル うまくいきません。false
サブシェルに制限され、親シェルに伝播されることはありません。ここでエラー処理を行うには、独自のロジックを追加して(false) || false
を実行しますset -e (false) echo survived
-
trap
でも同じことが起こります また。上記の理由により、以下のロジックは機能しません。trap 'echo error' ERR (false)
基本的なエラー処理
テスト ケース ランナーが失敗したテストに対してゼロ以外のコードを返す場合は、単純に次のように記述できます。
test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
printf '%s\n' "Test case x failed" >&2 # write error message to stderr
exit 1 # or exit $test_result
fi
またはさらに短く:
if ! test_handler test_case_x; then
printf '%s\n' "Test case x failed" >&2
exit 1
fi
または最短:
test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }
test_handler の終了コードで終了するには:
test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }
高度なエラー処理
より包括的なアプローチを取りたい場合は、エラー ハンドラを使用できます。
exit_if_error() {
local exit_code=$1
shift
[[ $exit_code ]] && # do nothing if no error code passed
((exit_code != 0)) && { # do nothing if error code is 0
printf 'ERROR: %s\n' "[email protected]" >&2 # we can use better logging here
exit "$exit_code" # we could also check to make sure
# error code is numeric when passed
}
}
次に、テスト ケースを実行した後に呼び出します。
run_test_case test_case_x
exit_if_error $? "Test case x failed"
または
run_test_case test_case_x || exit_if_error $? "Test case x failed"
exit_if_error
のようなエラー ハンドラを持つ利点
- ロギング、スタック トレースの出力、通知、クリーンアップなど、すべてのエラー処理ロジックを 1 か所で標準化できます
- エラー ハンドラが引数としてエラー コードを取得するようにすることで、呼び出し元を
if
の煩雑さから解放できます。 エラーの終了コードをテストするブロック - (トラップを使用して) シグナル ハンドラーがある場合は、そこからエラー ハンドラーを呼び出すことができます
エラー処理およびロギング ライブラリ
エラー処理とロギングの完全な実装は次のとおりです:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
関連記事
- Bash でのエラー処理
- Bash Hackers Wiki の「caller」組み込みコマンド
- Linux に標準の終了ステータス コードはありますか?
- BashFAQ/105 - set -e (または set -o errexit または trap ERR) が期待どおりに動作しないのはなぜですか?
__FILE__
に相当 、__LINE__
バッシュで- Bash に TRY CATCH コマンドはありますか
- スタック トレースをエラー ハンドラに追加するには、次の投稿を参照してください:Bash スクリプトによって呼び出された実行済みプログラムのトレース
- シェル スクリプトの特定のエラーを無視する
- シェル パイプでエラー コードをキャッチする
- シェル スクリプト内でログの冗長性を管理するにはどうすればよいですか?
- Bash で関数名と行番号をログに記録する方法
- Bash では、単一の角括弧 [ ] よりも二重の角括弧 [[ ]] の方が望ましいですか?
これは、エラー メッセージを保存する場所によって異なります。
次のことができます:
echo "Error!" > logfile.log
exit 125
または以下:
echo "Error!" 1>&2
exit 64
例外を発生させると、プログラムの実行が停止します。
exit xxx
のようなものを使用することもできます どこで xxx
オペレーティング システムに返すエラー コードです (0 から 255)。こちら 125
と 64
終了できるランダムなコードです。プログラムが異常終了した (エラーが発生したなど) ことを OS に示す必要がある場合は、ゼロ以外の終了コード を渡す必要があります。 exit
へ .
@chepner が指摘したように、 exit 1
を実行できます 、これは不特定のエラーを意味します .