Bashスクリプトでエラーを発生させる


103

「テストケースが失敗しました!!!」というメッセージを含むBashスクリプトでエラーを発生させたいのですが。バッシュでこれを行う方法?

例えば:

if [ condition ]; then
    raise error "Test cases failed !!!"
fi

1
このエラーで何をしたいですか?スクリプトはどのように呼び出されますか?1つだけのスクリプトですか、それとも多くのスクリプトですか?スクリプトの使用方法はどのようになりますか?
Etan Reisner

スクリプトは1つだけです。私は./script/test.shのようなubuntu端末を使用してそれを呼び出しました
Naveen Kumar



愛はecho you screwed up at ... | mail -s BUG $bugtrackeremailaddressないの?
2018年

回答:


121

これは、エラーメッセージを保存する場所によって異なります。

次のことができます。

echo "Error!" > logfile.log
exit 125

または次のようにします。

echo "Error!" 1>&2
exit 64

例外を発生させると、プログラムの実行が停止します。

また、のようなものを使用することができますexit xxxどこxxxにエラーコードがあります(0〜255)のオペレーティングシステムに戻りたいことがあります。ここに12564あなたが終了できるランダムコードがあります。プログラムが異常停止した(たとえば、エラーが発生した)ことをOSに示す必要がある場合は、ゼロ以外の終了コードをに渡す必要がありますexit

@chepnerが指摘したように、実行できますexit 1。これは、詳細不明のエラーを意味します


12
または、エラーが発生するはずのstderrに送信することもできます。

stderrに送信する方法は?
Naveen Kumar

2
@ user3078630、私の回答を編集しました。1>&2トリックを実行します
ForceBru

エラーの場合、ゼロ以外の終了ステータスで終了する必要があります。exitそれ自体で0であってもよい。最も最近完了したコマンドの終了ステータス、使用しています
chepner

3
特定の意味がない限りexit 1、を使用する必要があります。これは、慣例により、詳細不明のエラーを意味します。
chepner

36

基本的なエラー処理

テストケースランナーが失敗したテストに対してゼロ以外のコードを返す場合は、次のように記述するだけです。

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は次のとおりです。

  • ロギングスタックトレースの印刷、通知、クリーンアップなどのすべてのエラー処理ロジックを1か所で標準化できます
  • エラーハンドラーに引数としてエラーコードを取得させることで、エラーのif終了コードをテストするブロックの混乱から呼び出し元を解放できます。
  • シグナルハンドラーがある場合(trapを使用)、そこからエラーハンドラーを呼び出すことができます。

エラー処理とロギングライブラリ

エラー処理とロギングの完全な実装は次のとおりです。

https://github.com/codeforester/base/blob/master/lib/stdlib.sh


関連記事


9

この問題に対処する方法はいくつかあります。要件の1つが、いくつかのシェルコマンドを含むシェルスクリプト/関数を実行し、スクリプトが正常に実行されたかどうかを確認し、失敗した場合にエラーをスローすることを想定しています。

シェルコマンドは通常、返された終了コードに依存して、予期しないイベントが原因で成功したか失敗したかをシェルに通知します。

だからあなたがしたいことはこれら二つのカテゴリーに当てはまる

  • エラー時に終了
  • エラー時に終了してクリーンアップ

どちらを実行するかに応じて、使用できるシェルオプションがあります。最初のケースでは、シェルはオプションを提供set -eし、2番目のケースではtrapEXIT

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、を使用する代わりに、盲目的に終了する代わりにエラーを処理できるようにする場合trapERR疑似信号でa を使用します。

ERRトラップは、シェル自体が非ゼロのエラーコードで終了するが、場合条件(場合と同様の一部ではないことをシェルによって任意のコマンド実行時にコードを実行することはない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

これを、false15行目に含まれている単純なスクリプトにまとめると、取得する情報は

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)

5

これは、STDERRに失敗したものの最後の引数を出力し、失敗した行を報告し、行番号を終了コードとしてスクリプトを終了する単純なトラップです。これらは常に素晴らしいアイデアであるとは限りませんが、これはあなたが構築できるいくつかの創造的なアプリケーションを示しています。

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR

これをループ付きのスクリプトに入れてテストします。乱数のヒットを確認するだけです。実際のテストを使用する場合があります。保釈する必要がある場合は、スローするメッセージでfalse(トラップをトリガーする)を呼び出します。

詳細な機能については、トラップで処理関数を呼び出します。さらにクリーンアップを行う必要がある場合は、引数($ _)で常に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ないこと(したがって砂糖)が含まれており、トラップを作動させる他のものは少し愚かに見えるかもしれません。それでも、私はこの方法が好きです。


4

2つのオプションがあります。スクリプトの出力をファイルにリダイレクトする、スクリプトにログファイルを導入する、および

  1. 出力をファイルにリダイレクトする

ここでは、スクリプトが警告およびエラーメッセージを含むすべての必要な情報を出力すると想定します。その後、出力を任意のファイルにリダイレクトできます。

./runTests &> output.log

上記のコマンドは、標準出力とエラー出力の両方をログファイルにリダイレクトします。

このアプローチを使用すると、スクリプトにログファイルを導入する必要がないため、ロジックが少し簡単になります。

  1. スクリプトにログファイルを導入します

スクリプトに、ログファイルをハードコーディングして追加します。

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のより良いオプションです。また、複数のコンポーネントを含む大きなスクリプトを作成した場合は、各部分を異なる方法でログに記録することができ、2番目の方法が唯一の方法です。オプション。


3

エラーメッセージを処理する関数を記述して、コード全体がよりクリーンになるようにすると便利です。

# 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はナノ秒であり、後でマイクロ秒に切り捨てます)。

最初のオプションがゼロまたは正の整数の場合、それは終了コードになり、オプションのリストから削除します。次に、スクリプトの名前、「ERROR」という単語、および時間とともにメッセージを標準エラーに報告します(パラメーター拡張を使用して、ナノ秒をマイクロ秒に切り捨てたり、GNU時間以外の場合は、たとえば12:34:56.%Nに切り捨てたりします12:34:56)。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"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.