HOWTO · Batch

バッチ スクリプトでのエラー処理

このチュートリアルでは、バッチ スクリプトでのエラー処理について説明します。

このページの内容

Windowsの.batまたは.cmdファイルでエラーに対処するには、コマンドの終了コードをすぐに確認し、問題を報告するか、成功時の処理を続けるかを分岐します。バッチコマンドは慣例として、成功時に0、失敗時に0以外のコードを返します。ただし、0以外のコードが特定の状態を示すこともあります。現在のステータスは%ERRORLEVEL%で取得でき、IF ERRORLEVELは値が指定した数以上かどうかを調べます。

バッチのエラーレベルの仕組み

コマンドは終了すると終了コードを返します。慣例では0が成功を表し、0以外の値はコマンドが正常に完了しなかったことを示します。ただし、その値の具体的な意味はコマンドによって異なります。バッチスクリプトでは、このステータスを使って次の処理を決められます。

現在のコードを読むには%ERRORLEVEL%を使い、しきい値を確認するにはIF ERRORLEVEL nを使います。IF ERRORLEVEL nが真になるのは、現在のコードがnと等しい場合だけでなく、n以上の場合です。結果を確認したいコマンドの直後にステータスを調べてください。別のコマンドを実行すると、ステータスが置き換わることがあります。

コマンドの終了コードを確認する

複数の値をしきい値で確認する場合は、大きいしきい値から先に調べます。そうしないと、大きいコードがそれより小さい各しきい値にも該当してしまいます。値を直接比較するには、コマンドの直後に%ERRORLEVEL%を保存し、その保存した値を比較します。

この例では、値にreadyという文字列が含まれるかどうかを確認します。失敗側の分岐を確認するには、messageを別の単語に変更してください。保存する値は、echoコマンドで置き換わる前にfindstrから直接取得します。このWindowsのコマンドプロンプトの例は、提供されたパケットでは実行されていません。

@echo off
set "message=ready"
echo(%message%| findstr /c:"ready" >nul
set "command_status=%ERRORLEVEL%"
if "%command_status%"=="0" (
    echo Match found.
) else (
    echo Match not found.
)

かっこで囲んだブロックでは、%variable%形式の参照はブロックの解析時に展開されます。ステータスはブロックに入る前に保存するため、比較には保存済みの結果が使われます。ステータスを正確に扱うには、その保存値を比較してください。一方、IF ERRORLEVEL nは現在のステータスがn以上かどうかを調べます。

条件付きコマンドでエラーを処理する

&&は直前のコマンドが成功した場合にだけ次のコマンドを実行し、||は直前のコマンドが失敗した場合にだけ次のコマンドを実行します。短い分岐には、次の演算子が便利です。

some_command && echo Success || echo Failure

長い処理には、IFブロックのほうが読みやすくなります。IF ERRORLEVEL 1はコードが1以上かを確認します。一般的な0以外の失敗ステータスをまとめて扱えますが、それぞれの意味を区別することはできません。ステータスを正確に扱うには、上記のように%ERRORLEVEL%をすぐに保存し、その値を比較してください。

失敗と成功の例を作成する

このスクリプトは、名前をランダムにして、Windowsの一時ディレクトリ内に新しいデモ用ディレクトリを作成します。パスを削除する処理はありません。ディレクトリの作成に失敗した場合、または想定した入力ファイルがすでに存在する場合は、セットアップ用ステータス2で終了します。typeを実行する前に入力ファイルが存在しないことを確認し、ファイルがまだない場合に限って想定される失敗メッセージを表示します。その後、入力ファイルを作成し、書き込み処理の結果を確認してから同じファイルを読み取ります。このWindowsのコマンドプロンプトの例は、提供されたパケットでは実行されていません。

次の例を実行します。

@echo off
set "demo_dir=%TEMP%\batch-error-demo-%RANDOM%-%RANDOM%"
set "demo_file=%demo_dir%\input.txt"
if exist "%demo_dir%" (
    echo Setup failed: the randomized demo directory is already present.
    exit /b 2
)
mkdir "%demo_dir%" >nul 2>&1
if errorlevel 1 (
    echo Setup failed: could not create the fresh demo directory.
    exit /b 2
)
if exist "%demo_file%" (
    echo Setup failed: the demo input is already present.
    exit /b 2
)

type "%demo_file%" >nul 2>&1
set "command_status=%ERRORLEVEL%"
if not "%command_status%"=="0" (
    if not exist "%demo_file%" (
        echo Failure: input.txt was not found.
    ) else (
        echo Failure: input.txt could not be read.
        exit /b 1
    )
) else (
    echo Read input.txt successfully.
)

>"%demo_file%" echo ready
if errorlevel 1 (
    echo Setup failed: could not create input.txt.
    exit /b 2
)
type "%demo_file%" >nul 2>&1
if errorlevel 1 (
    echo Failure: input.txt could not be read.
    exit /b 1
)
echo Success: input.txt contains ready.
exit /b 0

最初のチェックはファイルがない場合の失敗を、後続の読み取りは成功を示すように構成されています。読み取りに成功するとスクリプトは0を返し、読み取りに失敗すると1を返します。新しいデモ用ディレクトリまたは入力ファイルを用意できない場合は2を返します。無関係なデータを削除しないよう、一意の名前を付けたディレクトリは%TEMP%に残します。後片付けが必要な場合は、内容を確認してから、そのディレクトリだけを手動で削除してください。

エラーをログに記録し、有用な終了ステータスを返す

有用なエラーハンドラーは、失敗した操作を特定し、必要に応じて診断用の出力を保存して、呼び出し元に0以外のステータスを返します。標準出力と標準エラー出力の両方をログに追記するには>> "%LOGFILE%" 2>&1を使い、その直後にコマンドのステータスを確認します。リダイレクトは出力を記録するだけで、それ自体が失敗を処理するわけではありません。

たとえば、適切なログのパスを設定した後、スクリプトで>> "%LOGFILE%" 2>&1を付けてコマンドを実行し、次の行で%ERRORLEVEL%を保存して、その値に応じて分岐できます。失敗時は、操作名とログファイルを示すメッセージを表示してからexit /b 1を使い、成功時はexit /b 0を使います。スクリプトを実行するユーザーが書き込めるログのパスを選び、0以外のコードがすべて同じ原因を示すと決めつけないでください。