バッチファイルやコマンドプロンプトで ERRORLEVEL を使ってエラーハンドリングを行う方法を解説します。
ERRORLEVEL は直前に実行されたコマンドの終了コード(戻り値)を表す特殊な変数で、コマンドが正常に完了したかどうかを判定する、バッチファイルのエラー処理の基本です。
この記事では、基本的な判定方法から、%ERRORLEVEL% と IF ERRORLEVEL の違い、&& / || を使った簡潔な書き方、遅延展開が必要なケース、主要コマンドの終了コード一覧、実務で使えるテンプレートまで網羅的に解説します。
ERRORLEVELとは?エラーハンドリングの基本
ERRORLEVEL とは、直前に実行されたコマンドやプログラムが返す終了コード(Exit Code)のことです。
| 終了コード | 意味 |
|---|---|
0 |
正常終了(成功) |
1以上 |
異常終了(失敗) |
バッチファイルのエラーハンドリングとは、この終了コードを検知して処理を中断・分岐・ログ出力することを指します。
代表的なエラー検知方法は以下のとおりです。
ERRORLEVELを使った終了コード判定(本記事で解説)if existによるファイル存在チェック&&/||によるコマンド連結(後述)
ERRORLEVELの基本的なチェック方法
%ERRORLEVEL% を if 文で判定するのが最も基本的な方法です。
@echo off
rem ファイルをコピー
copy C:\data\input.txt C:\backup\input.txt
rem 直前のコマンドが成功したか確認
if %ERRORLEVEL% equ 0 (
echo コピーが成功しました。
) else (
echo コピーに失敗しました。エラーコード: %ERRORLEVEL%
)
ERRORLEVEL が 0 なら成功、それ以外なら失敗と判定します。
neq 0 で判定するのがベストプラクティス
エラー判定では equ 1 ではなく neq 0(0以外)を使いましょう。
rem NG: エラーコード2や3を見逃す if %ERRORLEVEL% equ 1 (echo エラー) rem OK: 0以外はすべてエラーとして検知 if %ERRORLEVEL% neq 0 (echo エラー)
エラーコードは 1 とは限りません。コマンドによって 2、3 など異なる値を返すため、neq 0 で判定するのが安全です。
ERRORLEVELの判定がうまくいかない場合
%ERRORLEVEL% の判定には書き方によって挙動が変わる落とし穴があります。判定がうまくいかない場合は「if ERRORLEVELが正しく判定されない理由」も確認してください。
特定のエラーコードごとに処理を分岐する
複数のエラーコードに対して異なる処理を行いたい場合は、else if で分岐します。
@echo off
robocopy C:\source C:\backup /MIR
if %ERRORLEVEL% equ 0 (
echo 変更なし。コピーの必要はありませんでした。
) else if %ERRORLEVEL% equ 1 (
echo コピーが正常に完了しました。
) else if %ERRORLEVEL% equ 2 (
echo 余分なファイルが検出されました。
) else if %ERRORLEVEL% geq 8 (
echo エラーが発生しました。エラーコード: %ERRORLEVEL%
exit /b %ERRORLEVEL%
)
robocopy は他のコマンドと異なり、成功時でも 0 以外の終了コードを返します。0=変更なし、1=コピー成功、8以上=エラーです。このようにコマンドごとに終了コードの意味が異なるため、事前に確認しておきましょう。
ERRORLEVELを使った制御フロー(goto / exit /b)
エラー発生時に処理を中断したい場合は、goto でエラー処理にジャンプするか、exit /b でバッチを終了します。
gotoでエラー処理にジャンプ
@echo off
echo === バックアップ処理 開始 ===
rem 1. ソースフォルダの存在確認
if not exist C:\data\ (
echo エラー: ソースフォルダが存在しません。
goto :error
)
rem 2. ファイルをコピー
xcopy C:\data\*.* C:\backup\ /E /Y
if %ERRORLEVEL% neq 0 (
echo エラー: ファイルコピーに失敗しました。
goto :error
)
rem 3. 完了ログを出力
echo %date% %time% バックアップ成功 >> C:\logs\backup.log
echo === バックアップ処理 正常終了 ===
goto :end
:error
echo %date% %time% バックアップ失敗 >> C:\logs\backup.log
echo === バックアップ処理 異常終了 ===
exit /b 1
:end
exit /b 0
各ステップでエラーを検知し、失敗した時点で :error ラベルにジャンプします。
exit /b で終了コードを返す
exit /b に数値を渡すと、呼び出し元のバッチファイルから ERRORLEVEL として参照できます。
rem sub.bat(呼び出される側) @echo off copy %1 %2 if %ERRORLEVEL% neq 0 exit /b 1 exit /b 0
rem main.bat(呼び出す側)
@echo off
call sub.bat C:\data\file.txt C:\backup\file.txt
if %ERRORLEVEL% neq 0 (
echo sub.bat がエラーで終了しました。
)
エラー時の処理パターンの詳細は「エラー時に処理を中断・終了する方法」をご覧ください。
比較演算子の使い方
ERRORLEVEL の条件分岐で使う比較演算子の一覧です。
| 演算子 | 意味 | 使用例 |
|---|---|---|
equ |
等しい(equal) | if %ERRORLEVEL% equ 0 |
neq |
等しくない(not equal) | if %ERRORLEVEL% neq 0 |
lss |
より小さい(less) | if %ERRORLEVEL% lss 8 |
leq |
以下(less or equal) | if %ERRORLEVEL% leq 3 |
gtr |
より大きい(greater) | if %ERRORLEVEL% gtr 0 |
geq |
以上(greater or equal) | if %ERRORLEVEL% geq 8 |
%ERRORLEVEL% と IF ERRORLEVEL の違い
ERRORLEVELの判定には2つの書き方があり、挙動が異なります。
%ERRORLEVEL%(変数展開)
if %ERRORLEVEL% equ 0 (echo 成功) if %ERRORLEVEL% equ 1 (echo エラー1) if %ERRORLEVEL% equ 2 (echo エラー2)
%ERRORLEVEL% は終了コードの値そのものを展開します。equ、neq などの比較演算子と組み合わせて、特定の値と一致するかを判定します。
IF ERRORLEVEL n(レガシー構文)
if ERRORLEVEL 1 (echo エラー(終了コード1以上))
この形式は ERRORLEVEL が n 以上のときに真になります。equ(等しい)ではなく geq(以上)と同じ動きです。
2つの書き方の比較
%ERRORLEVEL% |
IF ERRORLEVEL n |
|
|---|---|---|
| 判定方式 | 値を展開して比較 | n 以上かどうか |
| 特定の値と一致判定 | 可能(equ, neq等) | 不可(以上判定のみ) |
| 遅延展開の影響 | 受ける(後述) | 受けない |
| 推奨度 | 推奨 | レガシー |
基本的には %ERRORLEVEL% を使いましょう。ただし、for 文や if 文のブロック内では遅延展開の問題があります(次節で解説)。
&& と || を使ったエラーハンドリング
&&(成功時に実行)と ||(失敗時に実行)を使うと、1行で簡潔にエラー処理を書けます。
rem 成功したら次のコマンドを実行 copy file.txt backup.txt && echo コピー成功 rem 失敗したらエラーメッセージを表示 copy file.txt backup.txt || echo コピー失敗 rem 組み合わせ:成功時と失敗時で処理を分ける copy file.txt backup.txt && echo 成功 || echo 失敗
複数コマンドの連続実行
&& を使えば、前のコマンドが成功した場合だけ次のコマンドを実行できます。
rem 3つのコマンドを順番に実行(途中で失敗したら停止) mkdir C:\backup && copy C:\data\*.* C:\backup\ && echo 全処理完了
if %ERRORLEVEL% を毎回書くよりもシンプルで、ワンライナーのバッチ処理に便利です。
遅延展開(enabledelayedexpansion)が必要なケース
for 文や if 文の括弧ブロック内で %ERRORLEVEL% を使うと、期待通りに動かないことがあります。
問題のあるコード
@echo off
rem NG: for文の中で %ERRORLEVEL% を参照しても値が更新されない
for %%f in (a.txt b.txt c.txt) do (
copy %%f C:\backup\
if %ERRORLEVEL% neq 0 (
echo %%f のコピーに失敗しました。
)
)
このコードでは、%ERRORLEVEL% が for ブロックの実行前に展開されるため、ループ内で更新されません。
遅延展開で解決する
@echo off
setlocal enabledelayedexpansion
for %%f in (a.txt b.txt c.txt) do (
copy %%f C:\backup\
if !ERRORLEVEL! neq 0 (
echo %%f のコピーに失敗しました。
)
)
endlocal
setlocal enabledelayedexpansion を宣言し、% の代わりに ! で囲む(!ERRORLEVEL!)ことで、実行時に最新の値が展開されます。
遅延展開が不要な代替手法
if ERRORLEVEL n 形式(レガシー構文)は遅延展開なしでも正しく動作します。
@echo off
for %%f in (a.txt b.txt c.txt) do (
copy %%f C:\backup\
if ERRORLEVEL 1 (
echo %%f のコピーに失敗しました。
)
)
遅延展開を使いたくない場合の代替手段として覚えておくと便利です。
主要コマンドのERRORLEVEL(終了コード)一覧
よく使うコマンドの終了コードをまとめました。
| コマンド | 成功 | 失敗 | 備考 |
|---|---|---|---|
copy |
0 | 1 | ファイルが見つからない場合に1 |
xcopy |
0 | 1〜5 | 1=ファイルなし、2=Ctrl+C中断、4=初期化エラー、5=書き込みエラー |
robocopy |
0〜7 | 8以上 | 0=変更なし、1=コピー成功。8以上がエラー |
del |
0 | 0 | 失敗してもERRORLEVELが変わらない |
mkdir |
0 | 1 | 既に存在する場合に1 |
ping |
0 | 1 | 応答なしの場合に1 |
net use |
0 | 2 | 接続失敗時に2 |
findstr |
0 | 1〜2 | 0=一致あり、1=一致なし、2=ファイルエラー |
reg query |
0 | 1 | キーが見つからない場合に1 |
注意:del コマンドは削除に失敗しても ERRORLEVEL を変更しません。del のエラーを検知するには、削除後に if exist でファイルが残っているか確認する必要があります。
rem del はERRORLEVELが変わらないため、existで確認する
del C:\temp\old.txt 2>nul
if exist C:\temp\old.txt (
echo 削除に失敗しました。
)
ERRORLEVELが期待通りにならない場合の対処法は「ERRORLEVELが0以外になる原因と対処法」で詳しく解説しています。
ERRORLEVELをリセットする方法
ERRORLEVEL は直前のコマンドの結果で上書きされますが、明示的に 0 にリセットしたい場合は以下の方法を使います。
rem 方法1: cmd /c exit 0(最も確実) cmd /c exit 0 echo %ERRORLEVEL% rem → 0 rem 方法2: ver コマンド(常に0を返す) ver >nul echo %ERRORLEVEL% rem → 0
リセットが必要な場面
@echo off
rem あるコマンドがエラーになるが、処理は続行したい
findstr "keyword" data.txt >nul 2>&1
rem ↑ 見つからなくても処理を続ける
rem ERRORLEVELをリセットしないと、次の判定に影響する
cmd /c exit 0
rem 次のコマンドの判定に影響しない
copy source.txt dest.txt
if %ERRORLEVEL% neq 0 (
echo コピーに失敗しました。
)
実務で使えるエラーハンドリングのテンプレート
基本テンプレート(ログ出力付き)
@echo off
setlocal
set LOGFILE=C:\logs\batch_%date:~0,4%%date:~5,2%%date:~8,2%.log
echo ========================================>> %LOGFILE%
echo [%date% %time%] 処理開始>> %LOGFILE%
rem --- ステップ1: ファイルコピー ---
echo [%date% %time%] ファイルコピー開始>> %LOGFILE%
xcopy C:\data\*.csv C:\backup\ /Y /E
if %ERRORLEVEL% neq 0 (
echo [%date% %time%] エラー: ファイルコピー失敗 [code=%ERRORLEVEL%]>> %LOGFILE%
goto :error
)
echo [%date% %time%] ファイルコピー完了>> %LOGFILE%
rem --- ステップ2: 古いファイルを削除 ---
echo [%date% %time%] クリーンアップ開始>> %LOGFILE%
forfiles /P C:\backup /S /M *.csv /D -30 /C "cmd /c del @path" 2>nul
echo [%date% %time%] クリーンアップ完了>> %LOGFILE%
echo [%date% %time%] 全処理正常終了>> %LOGFILE%
goto :end
:error
echo [%date% %time%] 処理が異常終了しました>> %LOGFILE%
exit /b 1
:end
endlocal
exit /b 0
複数バッチの連続実行テンプレート
@echo off
setlocal
echo === 日次バッチ処理 開始 ===
call step1_export.bat
if %ERRORLEVEL% neq 0 (
echo step1_export.bat でエラーが発生しました。後続処理を中止します。
exit /b 1
)
call step2_transform.bat
if %ERRORLEVEL% neq 0 (
echo step2_transform.bat でエラーが発生しました。後続処理を中止します。
exit /b 2
)
call step3_import.bat
if %ERRORLEVEL% neq 0 (
echo step3_import.bat でエラーが発生しました。
exit /b 3
)
echo === 日次バッチ処理 正常終了 ===
endlocal
exit /b 0
呼び出し先ごとに異なる終了コード(1、2、3)を返すことで、どのステップで失敗したかを特定できます。
リトライ処理のテンプレート
ネットワーク処理など、一時的なエラーが起きやすい場合はリトライを組み込みます。
@echo off
setlocal
set RETRY_COUNT=0
set MAX_RETRY=3
:retry
set /a RETRY_COUNT+=1
echo 試行 %RETRY_COUNT%/%MAX_RETRY%...
ping -n 1 192.168.1.100 >nul 2>&1
if %ERRORLEVEL% equ 0 (
echo 接続成功。
goto :success
)
if %RETRY_COUNT% lss %MAX_RETRY% (
echo 接続失敗。10秒後にリトライします...
timeout /t 10 /nobreak >nul
goto :retry
)
echo %MAX_RETRY%回試行しましたが接続できませんでした。
exit /b 1
:success
echo 後続処理を実行します...
endlocal
exit /b 0
よくある質問(FAQ)
echo %ERRORLEVEL%で確認できます。内部コマンド(echo・set等)はERRORLEVELをリセットしない場合があります。if errorlevel 1は「ERRORLEVELが1以上ならtrue」という比較です。if %errorlevel% == 1は「ERRORLEVELが厳密に1のときだけtrue」です。推奨はif %errorlevel% neq 0(0でなければエラー)で、厳密に値をチェックしたい場合に変数形式を使います。if %errorlevel% neq 0 (echo エラーが発生しました & exit /b 1)を書きます。またはコマンド || goto :ERRORのパイプライン演算子を使う方法もシンプルです。スクリプト全体に適用するなら各コマンドの後にチェックを書くか、ラッパー関数を作成します。まとめ
ERRORLEVEL はバッチファイルのエラーハンドリングの基本です。
| やりたいこと | 書き方 |
|---|---|
| 成功/失敗を判定 | if %ERRORLEVEL% neq 0 (echo エラー) |
| 特定のコードで分岐 | if %ERRORLEVEL% equ 1 (...) else if %ERRORLEVEL% equ 2 (...) |
| 失敗時に中断 | if %ERRORLEVEL% neq 0 goto :error |
| 1行で簡潔に書く | copy a b && echo 成功 || echo 失敗 |
| ループ内で判定 | setlocal enabledelayedexpansion + !ERRORLEVEL! |
| ERRORLEVELをリセット | cmd /c exit 0 |
ポイント:
- エラー判定は
neq 0(0以外)で行う for/ifブロック内では遅延展開(!ERRORLEVEL!)を使うdelなど ERRORLEVELが変わらないコマンドがある点に注意robocopyのように成功でも0以外を返すコマンドがある- 実務ではログ出力と組み合わせてエラーの原因を追跡しやすくする
