「テストケースに失敗しました!!!」というメッセージとともにBashスクリプトでエラーを発生させたい。 Bashでこれを行う方法は?
例えば:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
これは、エラーメッセージを保存する場所によって異なります。
次のことができます。
echo "Error!" > logfile.log
exit 125
または以下:
echo "Error!" 1>&2
exit 64
例外を発生させると、プログラムの実行が停止します。
exit xxx
のようなものを使用することもできます。ここで、xxx
は、オペレーティングシステムに返すエラーコード(0〜255)です。ここで、125
と64
は、終了できるランダムなコードです。プログラムが異常に停止した(たとえば、エラーが発生した)ことをOSに示す必要がある場合は、ゼロ以外の終了コードをexit
に渡す必要があります。
@chepner 指摘 のように、exit 1
を実行できます。これは、不特定のエラーを意味します。
テストケースランナーが失敗したテストに対して 非ゼロコード を返す場合、次のように書くことができます。
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' "$@" >&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
のようなエラーハンドラを使用する利点は次のとおりです。
if
ブロックの混乱から呼び出し元を解放できます。エラー処理とロギングの完全な実装は次のとおりです。
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
__FILE__
、__LINE__
と同等この問題に対処するには、さらにいくつかの方法があります。要件の1つは、いくつかのシェルコマンドを含むシェルスクリプト/関数を実行し、スクリプトが正常に実行されたかどうかを確認し、失敗した場合はエラーをスローすることです。
シェルコマンドは通常、返された終了コードに依存して、シェルに予期しないイベントが原因で成功したか失敗したかを通知します。
あなたがしたいことは、これらの2つのカテゴリーに分類されます
どちらを実行するかに応じて、使用可能なシェルオプションがあります。前者の場合、シェルはset -e
のオプションを提供し、後者の場合はtrap
でEXIT
を実行できます
exit
を使用する必要がありますか?exit
を使用すると、一般に読みやすさが向上します。特定のルーチンでは、答えがわかれば、すぐに呼び出しルーチンに戻ります。エラーが検出された後、さらにクリーンアップを必要としないようにルーチンが定義されている場合、すぐに終了しないということは、さらにコードを記述する必要があることを意味します。
そのため、スクリプトの終了をクリーンにするためにスクリプトでクリーンアップアクションを実行する必要がある場合は、notを使用して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
15行目にfalse
を含む単純なスクリプトにこれをまとめると、
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マニュアルページ にあります。
別の注意点は、サブシェルを扱っている場合、提供されているメソッドはどれも機能しないことを理解することです。その場合、独自のエラー処理を追加する必要があります。
set -e
のサブシェルでは機能しません。 false
はサブシェルに制限されており、親シェルに伝搬されることはありません。ここでエラー処理を行うには、独自のロジックを追加して(false) || false
を実行します
set -e
(false)
echo survived
trap
でも同じことが起こります。上記の理由により、以下のロジックは機能しません。
trap 'echo error' ERR
(false)
STDERRに失敗したものの最後の引数を出力し、失敗した行を報告し、行番号を終了コードとしてスクリプトを終了する簡単なトラップを次に示します。これらは常に素晴らしいアイデアではありませんが、これはあなたが構築できる創造的なアプリケーションを示しています。
trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
それをテストするためのループを備えたスクリプトに入れました。いくつかの乱数のヒットをチェックします。実際のテストを使用できます。救済する必要がある場合は、スローするメッセージでfalse(トラップをトリガーする)を呼び出します。
詳細な機能については、トラップが処理関数を呼び出すようにします。さらにクリーンアップなどを行う必要がある場合は、arg($ _)でいつでもcaseステートメントを使用できます。少し構文的なシュガーのvarに割り当てます-
trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false
while :
do x=$(( $RANDOM % 10 ))
case $x in
0) $throw "DIVISION BY ZERO" ;;
3) $raise "MAGIC NUMBER" ;;
*) echo got $x ;;
esac
done
サンプル出力:
# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6
明らかに、あなたはできる
runTest1 "Test1 fails" # message not used if it succeeds
設計を改善する余地がたくさんあります。
欠点はfalse
がきれいではないという事実(したがって砂糖)を含んでおり、トラップをトリッピングする他のことは少し愚かに見えるかもしれません。それでも、私はこの方法が好きです。
コードが全体的にきれいになるように、エラーメッセージを処理する関数を作成すると便利なことがよくあります。
# Usage: die [exit_code] [error message]
die() {
local code=$? now=$(date +%T.%N)
if [ "$1" -ge 0 ] 2>/dev/null; then # assume $1 is an error code if numeric
code="$1"
shift
fi
echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
exit $code
}
これは、前のコマンドからエラーコードを取得し、スクリプト全体を終了するときにデフォルトのエラーコードとして使用します。また、サポートされている場合はマイクロ秒で時刻を記録します(GNU日付の%N
はナノ秒であり、マイクロ秒に切り捨てます)。
最初のオプションがゼロまたは正の整数の場合、それが終了コードになり、オプションのリストから削除されます。次に、スクリプトの名前、Wordの「ERROR」、および時間を使用して、メッセージを標準エラーに報告します(パラメーター拡張を使用して、ナノ秒からマイクロ秒、またはGNU以外の時間、たとえば12:34:56.%N
から12:34:56
を切り捨てます) 。 Word ERRORの後にコロンとスペースが追加されますが、提供されたエラーメッセージがある場合のみです。最後に、以前に決定した終了コードを使用してスクリプトを終了し、通常どおりトラップをトリガーします。
いくつかの例(コードがscript.sh
にあると仮定):
if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"
$command |grep -q condition || die 1 "'$command' lacked 'condition'"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"
$command || die
# exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"
次の2つのオプションがあります。スクリプトの出力をファイルにリダイレクトする、スクリプトにログファイルを導入する、
ここでは、警告およびエラーメッセージを含む、必要なすべての情報をスクリプトが出力すると仮定します。その後、出力を任意のファイルにリダイレクトできます。
./runTests &> output.log
上記のコマンドは、標準出力とエラー出力の両方をログファイルにリダイレクトします。
このアプローチを使用すると、スクリプトにログファイルを導入する必要がないため、ロジックが少し簡単になります。
スクリプトで、ハードコーディングしてログファイルを追加します。
logFile='./path/to/log/file.log'
またはパラメータで渡す:
logFile="${1}" # This assumes the first parameter to the script is the log file
スクリプトの上部にあるログファイルに、実行時のタイムスタンプを追加することをお勧めします。
date '+%Y%-m%d-%H%M%S' >> "${logFile}"
その後、エラーメッセージをログファイルにリダイレクトできます。
if [ condition ]; then
echo "Test cases failed!!" >> "${logFile}";
fi
これにより、ログファイルにエラーが追加され、実行が継続されます。重大なエラーが発生したときに実行を停止する場合、スクリプトをexit
できます:
if [ condition ]; then
echo "Test cases failed!!" >> "${logFile}";
# Clean up if needed
exit 1;
fi
exit 1
は、不特定のエラーによりプログラムが実行を停止することを示すことに注意してください。必要に応じてこれをカスタマイズできます。
このアプローチを使用して、ログをカスタマイズし、スクリプトのコンポーネントごとに異なるログファイルを作成できます。
比較的小さなスクリプトがある場合、または他の人のスクリプトを変更せずに実行したい場合、最初のアプローチがより適しています。
ログファイルを常に同じ場所に配置する場合は、2の方が適切なオプションです。また、複数のコンポーネントを含む大きなスクリプトを作成した場合は、各部分を別々にログに記録することができます。オプション。