Linux で Bash スクリプトのエラーをトラップする方法
公開: 2022-08-27デフォルトでは、Linux の Bash スクリプトはエラーを報告しますが、実行を続けます。 次に何が必要かを判断できるように、自分でエラーを処理する方法を示します。
スクリプトでのエラー処理
エラー処理はプログラミングの一部です。 完璧なコードを書いたとしても、エラー状態に陥ることがあります。 ソフトウェアのインストールとアンインストール、ディレクトリの作成、アップグレードと更新の実行に伴い、コンピュータの環境は時間の経過とともに変化します。
たとえば、以前は問題なく実行されていたスクリプトが、ディレクトリ パスが変更されたり、ファイルのアクセス許可が変更されたりすると、問題が発生する可能性があります。 Bash シェルのデフォルト アクションは、エラー メッセージを出力し、スクリプトの実行を続行することです。 これは危険なデフォルトです。
失敗したアクションが、スクリプト内で後で発生する他の処理またはアクションにとって重要である場合、その重要なアクションは成功しません。 それがどれほど悲惨なことになるかは、スクリプトが何をしようとしているのかによって異なります。
より堅牢なスキームは、エラーを検出し、シャットダウンするか障害状態を修復する必要がある場合にスクリプトを機能させます。 たとえば、ディレクトリまたはファイルが見つからない場合、スクリプトでそれらを再作成するだけで十分な場合があります。
スクリプトで回復できない問題が発生した場合は、スクリプトをシャットダウンできます。 スクリプトをシャットダウンする必要がある場合、一時ファイルを削除したり、エラー状態とシャットダウン理由をログ ファイルに書き込んだりするなど、必要なクリーンアップを実行する機会があります。
終了ステータスの検出
コマンドとプログラムは、終了時にオペレーティング システムに送信される値を生成します。 これは終了ステータスと呼ばれます。 エラーが発生しなかった場合は値がゼロになり、エラーが発生した場合はゼロ以外の値になります。
スクリプトが使用するコマンドの終了ステータス (リターン コードとも呼ばれます) を確認し、コマンドが成功したかどうかを判断できます。
Bash では、ゼロは true に相当します。 コマンドからの応答が true 以外の場合、問題が発生したことがわかり、適切なアクションを実行できます。
このスクリプトをエディターにコピーし、「bad_command.sh」という名前のファイルに保存します。
#!/ビン/バッシュ if ( ! bad_command ); それから echo "bad_command はエラーのフラグを立てました。" 1番出口 フィ
chmod
コマンドでスクリプトを実行可能にする必要があります。 これは、スクリプトを実行可能にするために必要な手順です。そのため、自分のマシンでスクリプトを試してみたい場合は、スクリプトごとに忘れずに実行してください。 いずれの場合も、適切なスクリプトの名前に置き換えてください。
chmod +x bad_command.sh
スクリプトを実行すると、予想されるエラー メッセージが表示されます。
./bad_command.sh
「bad_command」などのコマンドはなく、スクリプト内の関数の名前でもありません。 実行できないので、レスポンスはゼロではありません。 応答がゼロでない場合 (感嘆符はここでは論理NOT
演算子として使用されます)、 if
ステートメントの本体が実行されます。
実際のスクリプトでは、この例のようにスクリプトが終了するか、障害状態の修復を試みる可能性があります。
exit 1
行が冗長であるように見える場合があります。 結局、スクリプトには他に何もなく、とにかく終了します。 しかし、 exit
コマンドを使用すると、終了ステータスをシェルに戻すことができます。 このスクリプトが 2 番目のスクリプト内から呼び出された場合、その 2 番目のスクリプトは、このスクリプトでエラーが発生したことを認識します。
コマンドの終了ステータスで論理OR
演算子を使用し、最初のコマンドからゼロ以外の応答がある場合は、スクリプト内の別のコマンドまたは関数を呼び出すことができます。
コマンド_1 || コマンド_2
これは、最初のコマンドが実行されるか、2 OR
のコマンドが実行されるため機能します。 一番左のコマンドが最初に実行されます。 成功した場合、2 番目のコマンドは実行されません。 ただし、最初のコマンドが失敗すると、2 番目のコマンドが実行されます。 したがって、このようなコードを構造化できます。 これは「logical-or./sh」です。
#!/ビン/バッシュ error_handler() { echo "エラー: ($?) $1" 1番出口 } || 悪いコマンド || error_handler "bad_command が失敗しました。行: ${LINENO}"
error_handler
という関数を定義しました。 これにより、失敗したコマンドの終了ステータスが出力され、変数$?
に保持されます。 関数が呼び出されたときに渡されるテキスト行。 これは変数$1
に保持されます。 この関数は、終了ステータス 1 でスクリプトを終了します。
スクリプトはbad_command
を実行しようとしますが、これは明らかに失敗するため、論理OR
演算子の右側のコマンド||
、実行されます。 これはerror_handler
関数を呼び出し、失敗したコマンドの名前と、失敗したコマンドの行番号を含む文字列を渡します。
スクリプトを実行してエラー ハンドラー メッセージを確認し、echo を使用してスクリプトの終了ステータスを確認します。
./論理または.sh
エコー $?
小さなerror_handler
関数は、 bad_command
の実行試行の終了ステータス、コマンドの名前、および行番号を提供します。 これは、スクリプトをデバッグするときに役立つ情報です。
スクリプトの終了ステータスは 1 です。 error_handler
によって報告される 127 終了ステータスは、「コマンドが見つからない」ことを意味します。 必要に応じて、それをexit
コマンドに渡すことで、スクリプトの終了ステータスとして使用できます。
別の方法として、 error_handler
を展開して、終了ステータスのさまざまな値をチェックし、それに応じてさまざまなアクションを実行する方法があります。次のような構造を使用します。
exit_code=$? if [ $exit_code -eq 1 ]; それから echo "操作は許可されていません" elif [ $exit_code -eq 2 ]; それから echo "シェルビルトインの誤用" . . . elif [ $status -eq 128 ]; それから echo "無効な引数" フィ
set を使用して終了を強制する
エラーが発生するたびにスクリプトを終了させたいことがわかっている場合は、強制的に終了させることができます。 これは、エラーを検出するとすぐにスクリプトが終了するため、クリーンアップの可能性やそれ以上の損害を逃すことを意味します。
これを行うには、 -e
(エラー) オプションを指定してset
コマンドを使用します。 これにより、コマンドが失敗するかゼロより大きい終了コードが返されるたびに終了するようにスクリプトが指示されます。 また、 -E
オプションを使用すると、エラーの検出とトラップがシェル関数で確実に機能します。
初期化されていない変数もキャッチするには、 -u
(未設定) オプションを追加します。 パイプされたシーケンスでエラーが検出されるようにするには、 -o pipefail
オプションを追加します。 これがないと、コマンドのパイプ シーケンスの終了ステータスは、シーケンスの最後のコマンドの終了ステータスになります。 パイプ シーケンスの途中で失敗したコマンドは検出されません。 -o pipefail
オプションは、オプションのリストに含まれている必要があります。
スクリプトの先頭に追加するシーケンスは次のとおりです。
set -Eeuo pipefail
これは、「unset-var.sh」と呼ばれる短いスクリプトで、未設定の変数が含まれています。
#!/ビン/バッシュ set -Eeou パイプフェイル echo "$unset_variable" echo "この行が見えますか?"
スクリプトを実行すると、unset_variable が初期化されていない変数として認識され、スクリプトが終了します。
./unset-var.sh
2 番目のecho
コマンドは実行されません。
エラーのあるトラップの使用
Bash trap コマンドを使用すると、特定のシグナルが発生したときに呼び出されるコマンドまたは関数を指定できます。 通常、これは、Ctrl+C キーの組み合わせを押したときに発生するSIGINT
などのシグナルをキャッチするために使用されます。 このスクリプトは「sigint.sh」です。
#!/ビン/バッシュ trap "echo -e '\nTerminated by Ctrl+c'; exit" SIGINT カウンター=0 真実でありながら 行う echo "ループ番号:" $((++カウンター)) 睡眠 1 終わり
trap
コマンドには、 echo
コマンドとexit
コマンドが含まれています。 SIGINT
が発生したときにトリガーされます。 スクリプトの残りの部分は単純なループです。 スクリプトを実行して Ctrl+C を押すと、 trap
定義からのメッセージが表示され、スクリプトが終了します。
./sigint.sh
ERR
シグナルでtrap
を使用して、発生したエラーをキャッチできます。 これらは、コマンドまたは関数に渡すことができます。 これが「trap.sh」です。 error_handler
という関数にエラー通知を送信しています。
#!/ビン/バッシュ トラップ 'error_handler $? $LINENO' エラー error_handler() { echo "エラー: ($1) が $2 で発生しました" } 主要() { echo "main() 関数の内部" 悪いコマンド 2番目 三番 $ を終了しますか? } 2番目() { echo "main() の呼び出し後" echo "second() 関数の内部" } 三番() { echo "thirth() 関数の内部" } 主要
スクリプトの大部分は、 second
とthird
の関数を呼び出すmain
関数内にあります。 エラーが発生すると (この場合はbad_command
が存在しないため)、 trap
ステートメントはエラーをerror_handler
関数に送信します。 失敗したコマンドの終了ステータスと行番号をerror_handler
関数に渡します。
./trap.sh
error_handler
関数は、ターミナル ウィンドウにエラーの詳細を表示するだけです。 必要に応じて、 exit
コマンドを関数に追加して、スクリプトを終了させることができます。 または、一連のif/elif/fi
ステートメントを使用して、さまざまなエラーに対してさまざまなアクションを実行できます。
一部のエラーを修正できる場合もあれば、スクリプトを停止する必要がある場合もあります。
最後のヒント
エラーをキャッチすることは、多くの場合、問題が発生する可能性があることを先取りし、発生した場合にそれらの不測の事態を処理するコードを組み込むことを意味します。 これは、スクリプトの実行フローと内部ロジックが正しいことを確認することに加えて行われます。
このコマンドを使用してスクリプトを実行すると、Bash はスクリプトの実行時にトレース出力を表示します。
bash -x your-script.sh
Bash は、ターミナル ウィンドウにトレース出力を書き込みます。 各コマンドとその引数が表示されます (引数がある場合)。 これは、コマンドが展開された後、実行される前に発生します。
とらえどころのないバグを追跡するのに非常に役立ちます。
関連: Linux Bash スクリプトを実行する前に構文を検証する方法