バッチファイルのエラー判定といえば ERRORLEVEL が定番ですが、実務では ERRORLEVEL だけに頼ると誤判定や取りこぼしが発生する場面が少なくありません。
たとえば、以下のようなケースが実際の現場で問題になります。
| 問題のケース |
ERRORLEVEL の挙動 |
結果 |
| SET コマンドの失敗 |
ERRORLEVEL を変更しない |
エラーを見逃す |
| ECHO コマンド |
常に 0 を返す |
判定不可 |
| 外部ツールの独自エラーコード |
意図しない値が残る |
誤判定のリスク |
| ネットワーク接続タイムアウト |
0 を返す場合がある |
成功と誤認 |
| パイプ処理での複合コマンド |
最後のコマンドの値のみ |
途中のエラーを見逃す |
この記事では、ERRORLEVEL を使わず、または補助的に使いながらエラーを検知する実務で使える10以上のテクニックを、基本から応用まで体系的に解説します。
この記事で学べること
|| 演算子と && 演算子によるエラー検知
- 標準エラー出力のリダイレクトを活用した検知方法
- ファイル・ディレクトリの存在チェックによる判定
- ログファイルの内容解析(FIND / FINDSTR)
- 環境変数・レジストリの変化を利用した検知
- FOR コマンドによる出力パースとバリデーション
- WMIC / PowerShell との連携による高度なエラー検知
- 複数手法を組み合わせた実務向けエラーハンドリング設計
ERRORLEVELを使用する場合はERRORLEVEL を使ってエラーハンドリングを行う方法をご参照ください。
||(OR)演算子でエラーを即座に検知する
|| 演算子は、直前のコマンドが失敗した場合にのみ後続のコマンドを実行する制御演算子です。ERRORLEVEL を明示的にチェックする必要がなく、コードが簡潔になります。
基本構文
bat – || 演算子の基本構文
rem コマンドが失敗した場合にのみ echo が実行される
copy source.txt dest.txt || echo コピーに失敗しました
rem 失敗時にエラーコードを返して終了
mkdir output_dir || (echo ディレクトリ作成失敗 & exit /b 1)
実務での活用パターン
|| 演算子は、1行でエラー処理を書けるため、簡潔なスクリプトに最適です。
bat – || 演算子の実務活用
@echo off
setlocal
rem ファイルのコピー(失敗時はログ出力して終了)
copy /Y "C:\data\input.csv" "D:\backup\input.csv" || (
echo [%date% %time%] ERROR: ファイルコピー失敗 >> error.log
exit /b 1
)
rem ネットワークドライブの接続確認
net use Z: \\server\share || (
echo ネットワークドライブに接続できません
exit /b 2
)
rem サービスの起動確認
net start "MySQL" || (
echo MySQL サービスの起動に失敗しました
exit /b 3
)
注意:|| 演算子は内部コマンド(SET, ECHO など)の一部では期待通り動作しない場合があります。SET コマンドが変数名を誤って指定しても ERRORLEVEL は変わらず、|| は実行されません。
|| が効かないコマンドの一覧
| コマンド |
|| の動作 |
代替手段 |
SET |
失敗しても || が発火しない |
IF DEFINED で確認 |
ECHO |
常に成功扱い |
出力先の存在を確認 |
FOR |
ループ自体は成功 |
ループ後に結果を検証 |
IF |
条件式自体は常に成功 |
条件分岐内で処理 |
REM |
常に成功 |
(対応不要) |
&&(AND)演算子で成功時のみ処理を続行する
&& 演算子は、直前のコマンドが成功した場合にのみ後続のコマンドを実行します。処理の連鎖を安全に行う際に有効です。
基本構文
bat – && 演算子の基本構文
rem コマンド1が成功した場合のみコマンド2を実行
mkdir output_dir && echo ディレクトリ作成成功
rem 複数コマンドの連鎖
cd /d "C:\project" && git pull && echo 更新完了
&& と || を組み合わせた成功/失敗の分岐
&& と || を組み合わせることで、三項演算子のような分岐処理が実現できます。
bat – && と || の組み合わせ
rem 成功なら「OK」、失敗なら「NG」を表示
ping -n 1 google.com > nul && (
echo ネットワーク接続: OK
) || (
echo ネットワーク接続: NG
exit /b 1
)
実行結果(ネットワーク接続成功時)
ネットワーク接続: OK
実務例: デプロイスクリプトでの連鎖実行
bat – デプロイスクリプトの連鎖処理
@echo off
setlocal
echo ===== デプロイ開始 =====
rem Step1: ビルド → Step2: テスト → Step3: デプロイ
call build.bat && (
echo [Step1] ビルド成功
call test.bat && (
echo [Step2] テスト成功
call deploy.bat && (
echo [Step3] デプロイ成功
) || echo [Step3] デプロイ失敗
) || echo [Step2] テスト失敗
) || echo [Step1] ビルド失敗
ポイント:&& の連鎖では、途中で失敗すると以降のステップがすべてスキップされます。これにより「ビルド失敗なのにデプロイが実行される」という事故を防げます。
|| と && の比較まとめ
| 演算子 |
実行条件 |
主な用途 |
記述例 |
&& |
直前が成功 |
成功時の続行・連鎖処理 |
cmd1 && cmd2 |
|| |
直前が失敗 |
失敗時のエラー処理 |
cmd1 || echo NG |
& |
無条件 |
単純な連続実行 |
cmd1 & cmd2 |
標準エラー出力(stderr)の有無で判定する
コマンドが標準エラー出力(stderr / ファイルディスクリプタ2)にメッセージを出す場合、その有無を利用してエラーを検知できます。ERRORLEVEL が常に 0 を返すツールでも、stderr に出力があれば異常を検知できるケースがあります。
基本: stderr をファイルにリダイレクトして判定
bat – stderr リダイレクトの基本
@echo off
setlocal
rem stderr をファイルに出力
somecommand 2> error.log
rem ファイルサイズで stderr の有無を判定
for %%A in (error.log) do if %%~zA neq 0 (
echo 標準エラー出力が検出されました
type error.log
exit /b 1
)
echo 正常終了
%%~zA の意味
%%~zA は FOR 変数 %%A のファイルサイズ(バイト数)を返す修飾子です
- ファイルが空(0バイト)なら stderr への出力がなかったと判断できます
- この構文はバッチファイル内でのみ有効です(コマンドプロンプトでは
%~zA)
stdout と stderr を分離してキャプチャする
標準出力と標準エラー出力を別々のファイルにリダイレクトすることで、正常な出力とエラーメッセージを分離できます。
bat – stdout と stderr の分離
@echo off
setlocal
rem stdout → result.log, stderr → error.log
somecommand > result.log 2> error.log
rem stderr が空でなければエラー
for %%A in (error.log) do if %%~zA neq 0 (
echo [ERROR] 以下のエラーが発生しました:
type error.log
exit /b 1
)
rem stdout の内容を処理
echo 処理結果:
type result.log
stderr の内容をフィルタリングして判定する
一部のコマンドは、警告レベルのメッセージも stderr に出力します。すべてをエラー扱いにすると誤検知が増えるため、特定のキーワードでフィルタリングする方法が有効です。
bat – stderr のフィルタリング
@echo off
setlocal
somecommand 2> error.log
rem 「fatal」「critical」を含む行があればエラーとみなす
findstr /i "fatal critical" error.log > nul && (
echo 致命的なエラーが検出されました
exit /b 1
)
rem 「warning」のみなら警告として続行
findstr /i "warning" error.log > nul && (
echo [WARNING] 警告が出力されましたが処理は続行します
)
echo 処理完了
注意:すべてのコマンドが stderr にエラーメッセージを出力するわけではありません。robocopy はエラー情報も stdout に出力するため、この方法では検知できません。対象コマンドの出力仕様を事前に確認してください。
ファイルやディレクトリの存在で判定する
処理結果としてファイルやディレクトリが生成される場合、その存在有無で成功・失敗を判断できます。ERRORLEVEL に依存しないため、コマンド仕様の違いを意識せずに済む点がメリットです。
基本: IF EXIST / IF NOT EXIST
bat – ファイル存在チェックの基本
@echo off
setlocal
rem コマンドを実行
somecommand
rem 出力ファイルの存在で成否を判定
if not exist "output.txt" (
echo 出力ファイルが生成されませんでした
exit /b 1
)
echo 処理成功: output.txt が生成されました
ファイルサイズの検証
ファイルが存在していても中身が空(0バイト)の場合、処理が正常に完了していない可能性があります。ファイルサイズも合わせて検証すると信頼性が上がります。
bat – ファイルサイズの検証
@echo off
setlocal
somecommand > output.csv
rem ファイルの存在チェック
if not exist "output.csv" (
echo ファイルが存在しません
exit /b 1
)
rem ファイルサイズの検証
for %%A in ("output.csv") do (
if %%~zA equ 0 (
echo ファイルは存在しますが空です
exit /b 2
)
echo ファイルサイズ: %%~zA バイト
)
ディレクトリの存在確認
ディレクトリの存在を確認する場合は、パスの末尾に \ を付けるか NUL を使います。
bat – ディレクトリの存在確認
rem 方法1: パス末尾に \ を付ける
if exist "C:\output\" (
echo ディレクトリが存在します
)
rem 方法2: NUL を使う(より確実)
if exist "C:\output\NUL" (
echo ディレクトリが存在します
)
実務例: バックアップ処理のファイル検証
bat – バックアップ処理のファイル検証
@echo off
setlocal
set BACKUP_DIR=D:\backup\%date:~0,4%%date:~5,2%%date:~8,2%
set SOURCE=C:\data\important.db
rem バックアップディレクトリの作成
if not exist "%BACKUP_DIR%" mkdir "%BACKUP_DIR%"
rem ソースファイルの存在確認
if not exist "%SOURCE%" (
echo [ERROR] バックアップ元が見つかりません: %SOURCE%
exit /b 1
)
rem コピー実行
copy /Y "%SOURCE%" "%BACKUP_DIR%\"
rem コピー結果の検証(ファイルサイズ比較)
for %%S in ("%SOURCE%") do set SRC_SIZE=%%~zS
for %%D in ("%BACKUP_DIR%\important.db") do set DST_SIZE=%%~zD
if not "%SRC_SIZE%"=="%DST_SIZE%" (
echo [ERROR] ファイルサイズが一致しません(元: %SRC_SIZE% / 先: %DST_SIZE%)
exit /b 2
)
echo [OK] バックアップ完了(%DST_SIZE% bytes)
ポイント:ファイルの存在だけでなく、ファイルサイズの一致も確認することで、コピーが途中で失敗するケース(ディスク容量不足など)も検知できます。
ワイルドカードを使った複数ファイルの検証
bat – ワイルドカードでの検証
@echo off
setlocal enabledelayedexpansion
rem 期待するファイルの数
set EXPECTED_COUNT=5
set ACTUAL_COUNT=0
rem 出力ファイルを数える
for %%F in (output\*.csv) do set /a ACTUAL_COUNT+=1
if !ACTUAL_COUNT! lss %EXPECTED_COUNT% (
echo [ERROR] ファイル数が不足しています(期待: %EXPECTED_COUNT% / 実際: !ACTUAL_COUNT!)
exit /b 1
)
echo [OK] %ACTUAL_COUNT% ファイル確認済み
ログファイルの内容解析でエラーを検知する(FIND / FINDSTR)
処理結果がログやテキストに出力される場合、その内容を解析して成否を判断する方法が有効です。特に、ERRORLEVEL が信用できないコマンドや、外部ツールの出力を確認したいケースで威力を発揮します。
FIND コマンドによる文字列検索
FIND コマンドは、指定した文字列がファイル内に存在するかを検索します。
bat – FIND で成功メッセージを検索
@echo off
setlocal
rem コマンド出力をログに保存
somecommand > result.log 2>&1
rem 成功メッセージの検索
find "SUCCESS" result.log > nul
if %ERRORLEVEL% neq 0 (
echo 成功メッセージが見つかりません
exit /b 1
)
rem エラーメッセージの検索
find "ERROR" result.log > nul
if %ERRORLEVEL% equ 0 (
echo エラーメッセージが検出されました
exit /b 2
)
echo 正常終了
FIND と FINDSTR の違い
FIND: 固定文字列の完全一致検索。シンプルで高速
FINDSTR: 正規表現対応。複雑なパターンマッチが可能
- 単純なキーワード検索には FIND、パターンマッチが必要なら FINDSTR を使う
FINDSTR コマンドによる正規表現検索
FINDSTR は正規表現をサポートしており、より柔軟なパターンマッチが可能です。
bat – FINDSTR による正規表現検索
@echo off
setlocal
somecommand > result.log 2>&1
rem 複数のエラーパターンを OR 検索
findstr /i /r "error fail abort exception" result.log > nul
if %ERRORLEVEL% equ 0 (
echo [ERROR] エラーパターンが検出されました:
findstr /i /r "error fail abort exception" result.log
exit /b 1
)
rem 行頭が「[ERROR]」で始まる行を検索
findstr /r "^\[ERROR\]" result.log > nul
if %ERRORLEVEL% equ 0 (
echo エラー行が見つかりました
exit /b 2
)
FINDSTR の便利なオプション一覧
| オプション |
意味 |
使用例 |
/i |
大文字小文字を区別しない |
findstr /i "error" |
/r |
正規表現を使用 |
findstr /r "^[0-9]" |
/c:"文字列" |
スペースを含む文字列を検索 |
findstr /c:"error code" |
/v |
一致しない行を表示 |
findstr /v "OK" |
/n |
行番号を表示 |
findstr /n "error" |
/s |
サブディレクトリも検索 |
findstr /s "TODO" *.bat |
実務例: ビルドログの解析
bat – ビルドログ解析の実務例
@echo off
setlocal enabledelayedexpansion
rem ビルド実行
call msbuild project.sln /p:Configuration=Release > build.log 2>&1
rem エラー数をカウント
set ERROR_COUNT=0
for /f %%A in ('findstr /i /c:"error " build.log ^| find /c /v ""') do set ERROR_COUNT=%%A
rem 警告数をカウント
set WARN_COUNT=0
for /f %%A in ('findstr /i /c:"warning " build.log ^| find /c /v ""') do set WARN_COUNT=%%A
echo ビルド結果: エラー %ERROR_COUNT% 件 / 警告 %WARN_COUNT% 件
if !ERROR_COUNT! gtr 0 (
echo [FAILED] ビルドエラーが検出されました
findstr /i /c:"error " build.log
exit /b 1
)
if !WARN_COUNT! gtr 10 (
echo [WARNING] 警告が多数あります(%WARN_COUNT%件)
)
echo [OK] ビルド成功
実行結果(エラー0件・警告3件の場合)
ビルド結果: エラー 0 件 / 警告 3 件
[OK] ビルド成功
FOR コマンドで出力をパースしてバリデーションする
FOR /F コマンドを使うと、コマンド出力やファイルの内容を1行ずつ解析できます。出力値が期待通りかどうかを動的に検証するのに最適な手法です。
基本: コマンド出力の取得と検証
bat – FOR /F でコマンド出力を取得
@echo off
setlocal enabledelayedexpansion
rem ディスクの空き容量を取得して検証
set FREE_SPACE=0
for /f "tokens=3" %%A in ('dir C:\ ^| findstr /c:"bytes free"') do set FREE_SPACE=%%A
echo 空き容量: %FREE_SPACE%
rem プロセスの実行状態を確認
set PROCESS_FOUND=0
for /f %%A in ('tasklist /fi "imagename eq notepad.exe" /nh ^| findstr /i "notepad"') do set PROCESS_FOUND=1
if !PROCESS_FOUND! equ 0 (
echo [ERROR] notepad.exe が実行されていません
exit /b 1
)
戻り値の数値検証
コマンドの出力から特定の数値を抽出し、閾値と比較することで異常を検知できます。
bat – 数値の閾値チェック
@echo off
setlocal enabledelayedexpansion
rem ping の応答時間を取得
set RESPONSE_TIME=0
for /f "tokens=5 delims==< " %%A in ('ping -n 1 google.com ^| findstr /i "time"') do (
set RESPONSE_TIME=%%A
)
rem 応答時間が 1000ms 以上なら警告
if !RESPONSE_TIME! gtr 1000 (
echo [WARNING] 応答時間が遅いです: !RESPONSE_TIME!ms
)
環境変数の変化を利用してエラーを検知する
処理の成功によって環境変数が設定・変更される場合、その変化を確認することで間接的にエラーを検知できます。
IF DEFINED による変数の存在確認
bat – IF DEFINED による環境変数チェック
@echo off
setlocal
rem 処理前にフラグ変数をクリア
set "RESULT="
rem 処理実行(成功時にRESULTを設定)
call some_process.bat
rem 変数の存在で成否を判定
if not defined RESULT (
echo [ERROR] 処理結果が設定されていません
exit /b 1
)
echo 処理結果: %RESULT%
変数値の比較による検証
bat – 変数値の比較
@echo off
setlocal enabledelayedexpansion
rem 処理前の値を保存
set "BEFORE=%MY_CONFIG%"
rem 設定更新処理を実行
call update_config.bat
rem 値が変化したか確認
if "!MY_CONFIG!"=="!BEFORE!" (
echo [WARNING] 設定値が変更されていません
)
レジストリの変化で判定する
インストーラーやシステム設定の変更では、レジストリの値が更新されます。REG QUERY を使ってレジストリ値の有無や内容を確認することで、処理の成否を判定できます。
bat – レジストリ値の確認
@echo off
setlocal
rem アプリケーションのインストール実行
start /wait installer.exe /silent
rem レジストリでインストール確認
reg query "HKLM\SOFTWARE\MyApp" /v "Version" > nul 2>&1
if %ERRORLEVEL% neq 0 (
echo [ERROR] インストールが完了していません
exit /b 1
)
rem バージョン値を取得して検証
for /f "tokens=3" %%A in ('reg query "HKLM\SOFTWARE\MyApp" /v "Version"') do set INSTALLED_VER=%%A
echo インストール済みバージョン: %INSTALLED_VER%
rem 期待するバージョンとの比較
if not "%INSTALLED_VER%"=="2.0.0" (
echo [ERROR] 期待するバージョン(2.0.0)と異なります
exit /b 2
)
echo [OK] インストール完了
注意:HKLM(HKEY_LOCAL_MACHINE)のレジストリを読み書きするには管理者権限が必要です。HKCU(HKEY_CURRENT_USER)は通常権限で操作できます。
WMIC を使った高度なシステム情報の検証
WMIC(Windows Management Instrumentation Command-line)を使うと、プロセスの状態、サービスの起動状況、ディスク情報などOS レベルの詳細な情報を取得してエラー検知に活用できます。
サービスの起動状態を確認する
bat – WMIC でサービス状態を確認
@echo off
setlocal enabledelayedexpansion
rem MySQL サービスの状態を取得
set SVC_STATE=Unknown
for /f "tokens=2 delims==" %%A in ('wmic service where "name='MySQL'" get state /value') do (
set "SVC_STATE=%%A"
)
rem 改行やスペースを除去
set "SVC_STATE=!SVC_STATE: =!"
if not "!SVC_STATE!"=="Running" (
echo [ERROR] MySQL サービスが起動していません(状態: !SVC_STATE!)
exit /b 1
)
echo [OK] MySQL サービスは正常に稼働中
プロセスの存在を確認する
bat – WMIC でプロセス確認
@echo off
setlocal enabledelayedexpansion
rem 対象プロセスが実行中か確認
set PROC_COUNT=0
for /f %%A in ('wmic process where "name='java.exe'" get processid /value ^| find /c "="') do set PROC_COUNT=%%A
if !PROC_COUNT! equ 0 (
echo [ERROR] Java プロセスが見つかりません
exit /b 1
)
echo [OK] Java プロセス: !PROC_COUNT! 個実行中
注意:WMIC は Windows 10 21H1 以降で「非推奨」とされています。新規スクリプトでは PowerShell の Get-WmiObject や Get-CimInstance の使用を検討してください。
PowerShell と連携した高度なエラー検知
バッチファイルから PowerShell を呼び出すことで、バッチ単体では難しい高度なエラー検知が可能になります。JSON のパース、HTTP リクエスト、イベントログの解析などを組み合わせることができます。
PowerShell コマンドの実行と結果の取得
bat – PowerShell でサービス状態を確認
@echo off
setlocal enabledelayedexpansion
rem PowerShell でサービスの状態を取得
for /f %%A in ('powershell -NoProfile -Command "(Get-Service 'MySQL').Status"') do set STATUS=%%A
if not "!STATUS!"=="Running" (
echo [ERROR] MySQL が停止しています: !STATUS!
exit /b 1
)
HTTP ステータスコードでの Web サービス監視
bat – PowerShell で HTTP ステータスコードを確認
@echo off
setlocal enabledelayedexpansion
set TARGET_URL=https://example.com/api/health
rem HTTP ステータスコードを取得
for /f %%A in ('powershell -NoProfile -Command "try { (Invoke-WebRequest -Uri '%TARGET_URL%' -UseBasicParsing -TimeoutSec 10).StatusCode } catch { $_.Exception.Response.StatusCode.value__ }"') do set HTTP_CODE=%%A
echo HTTP Status: !HTTP_CODE!
if not "!HTTP_CODE!"=="200" (
echo [ERROR] ヘルスチェック失敗(HTTP !HTTP_CODE!)
exit /b 1
)
echo [OK] サービスは正常に応答しています
イベントログの解析
bat – PowerShell でイベントログを確認
@echo off
setlocal enabledelayedexpansion
rem 直近1時間のエラーイベント数を取得
for /f %%A in ('powershell -NoProfile -Command "(Get-EventLog -LogName Application -EntryType Error -After (Get-Date).AddHours(-1) -ErrorAction SilentlyContinue).Count"') do set ERR_COUNT=%%A
if not defined ERR_COUNT set ERR_COUNT=0
if !ERR_COUNT! gtr 0 (
echo [WARNING] 直近1時間に !ERR_COUNT! 件のエラーイベントがあります
)
処理時間や応答タイムアウトで異常を推測する
想定より極端に早く終わる、またはタイムアウトする処理は、内部で異常が発生している可能性があります。処理時間を計測して閾値と比較する方法を紹介します。
処理時間の計測と閾値チェック
bat – 処理時間の計測
@echo off
setlocal enabledelayedexpansion
rem 開始時刻を記録(秒単位)
set START_TIME=%time%
for /f "tokens=1-3 delims=:." %%a in ("%START_TIME%") do (
set /a START_SEC=%%a*3600+%%b*60+%%c
)
rem 処理実行
call heavy_process.bat
rem 終了時刻を記録
set END_TIME=%time%
for /f "tokens=1-3 delims=:." %%a in ("%END_TIME%") do (
set /a END_SEC=%%a*3600+%%b*60+%%c
)
rem 経過時間(秒)
set /a ELAPSED=END_SEC-START_SEC
echo 処理時間: !ELAPSED! 秒
rem 異常に速い(5秒未満)場合は失敗の可能性
if !ELAPSED! lss 5 (
echo [WARNING] 処理が異常に速く終了しました(期待: 30秒以上)
)
rem タイムアウト(300秒超)の場合
if !ELAPSED! gtr 300 (
echo [ERROR] 処理がタイムアウトしました
exit /b 1
)
ポイント:処理時間が「短すぎる」場合も注意が必要です。本来30秒かかるバッチが1秒で終わった場合、入力データが読めなかったなど内部的にスキップしている可能性があります。
複数手法を組み合わせた実務向けエラーハンドリング設計
ここまで紹介した各手法は、それぞれ得意な検知領域があります。実務では複数の手法を組み合わせて多層防御を行うことで、信頼性の高いバッチファイルを構築できます。
各手法の得意領域まとめ
| 手法 |
得意な検知 |
苦手な検知 |
最適な場面 |
|| / && |
即座の成功/失敗判定 |
内部コマンドの失敗 |
外部コマンドの単純な分岐 |
| stderr リダイレクト |
警告・エラーメッセージ |
stderr を使わないコマンド |
外部ツールのエラー検知 |
| ファイル存在チェック |
成果物の確認 |
途中経過の異常 |
コピー・生成処理の検証 |
| ログ解析(FIND/FINDSTR) |
内容ベースの判定 |
ログを出さないツール |
ビルド・外部ツール連携 |
| 環境変数/レジストリ |
状態変化の検出 |
一時的な処理 |
インストーラー・設定変更 |
| WMIC / PowerShell |
OS レベルの詳細情報 |
実行速度(オーバーヘッド) |
サービス監視・HTTP チェック |
| 処理時間の計測 |
タイムアウト・異常終了 |
正常範囲の判断が曖昧 |
バッチの定期実行監視 |
実務例: 多層エラーチェック付きデータ処理バッチ
以下は、複数の検知手法を組み合わせた実務レベルのバッチファイルの例です。
bat – 多層エラーチェック付きバッチファイル
@echo off
setlocal enabledelayedexpansion
rem ==============================
rem データ処理バッチ(多層チェック版)
rem ==============================
set LOG_FILE=process_%date:~0,4%%date:~5,2%%date:~8,2%.log
set ERROR_FLAG=0
echo [%date% %time%] === 処理開始 === > %LOG_FILE%
rem --- Layer 1: 前提条件の確認 ---
if not exist "input\data.csv" (
echo [ERROR] 入力ファイルが見つかりません >> %LOG_FILE%
exit /b 1
)
rem --- Layer 2: || 演算子による即座のエラー検知 ---
copy /Y "input\data.csv" "work\data.csv" || (
echo [ERROR] ファイルコピー失敗 >> %LOG_FILE%
exit /b 2
)
rem --- Layer 3: stderr キャプチャ ---
call transform.bat "work\data.csv" > result.log 2> error.log
for %%A in (error.log) do if %%~zA neq 0 (
echo [ERROR] stderr 出力あり >> %LOG_FILE%
type error.log >> %LOG_FILE%
set ERROR_FLAG=1
)
rem --- Layer 4: 出力ファイルの検証 ---
if not exist "output\result.csv" (
echo [ERROR] 出力ファイルが生成されていません >> %LOG_FILE%
set ERROR_FLAG=1
)
rem --- Layer 5: ログ内容の解析 ---
findstr /i "error fatal exception" result.log > nul && (
echo [ERROR] 処理ログにエラーが含まれています >> %LOG_FILE%
set ERROR_FLAG=1
)
rem --- 最終判定 ---
if !ERROR_FLAG! equ 1 (
echo [%date% %time%] === 処理失敗 === >> %LOG_FILE%
exit /b 1
)
echo [%date% %time%] === 処理完了 === >> %LOG_FILE%
多層エラーチェックの設計指針
- Layer 1(前提条件): 入力ファイルや必要なディレクトリの存在確認
- Layer 2(即時検知):
|| 演算子による致命的エラーの即座の検知
- Layer 3(stderr): 標準エラー出力のキャプチャと内容確認
- Layer 4(成果物): 期待する出力ファイルの存在とサイズの検証
- Layer 5(ログ解析): 処理ログ内のエラーキーワード検索
実務例: robocopy の戻り値を正しくハンドリングする
robocopy は他のコマンドと異なり、成功時にも 0 以外の値を返す独特な仕様があります。ERRORLEVEL だけで判定すると誤検知するため、出力ログとの組み合わせが重要です。
| 戻り値 |
意味 |
判定 |
| 0 |
コピーなし(変更なし) |
正常 |
| 1 |
ファイルがコピーされた |
正常 |
| 2 |
余分なファイルあり |
正常(情報) |
| 4 |
不一致ファイルあり |
警告 |
| 8 |
コピー失敗あり |
エラー |
| 16 |
致命的エラー |
エラー |
bat – robocopy の正しいエラーハンドリング
@echo off
setlocal
robocopy "C:\source" "D:\dest" /MIR /LOG:robocopy.log
set RC=%ERRORLEVEL%
rem robocopy は 0-3 が正常、4-7 が警告、8以上がエラー
if %RC% geq 8 (
echo [ERROR] robocopy でエラーが発生しました(code: %RC%)
rem ログからエラー詳細を抽出
findstr /i "ERROR" robocopy.log
exit /b 1
)
if %RC% geq 4 (
echo [WARNING] robocopy で警告があります(code: %RC%)
)
echo [OK] robocopy 完了(code: %RC%)
よくあるミスとトラブルシューティング
実務でエラーハンドリングを実装する際に遭遇しがちなミスと、その解決方法をまとめます。
ミス1: 遅延展開を忘れてループ内の変数が展開されない
bat – NG: 遅延展開なし
@echo off
set COUNT=0
for %%F in (*.txt) do (
set /a COUNT+=1
rem ここで %COUNT% は常に 0 が表示される(展開済み)
echo %COUNT%
)
bat – OK: 遅延展開あり
@echo off
setlocal enabledelayedexpansion
set COUNT=0
for %%F in (*.txt) do (
set /a COUNT+=1
rem !COUNT! で遅延展開される
echo !COUNT!
)
ミス2: パイプ処理で前段のエラーを見逃す
パイプ(|)で接続したコマンドでは、ERRORLEVEL は最後のコマンドの結果のみを反映します。前段のコマンドが失敗していても検知できません。
bat – パイプのエラー検知問題と対策
rem NG: type が失敗しても findstr の結果で ERRORLEVEL が上書きされる
type missing_file.txt | findstr "pattern"
rem OK: 一旦ファイルに出力してから検索する
type data.txt > temp.txt 2> nul || (
echo ファイルの読み込みに失敗しました
exit /b 1
)
findstr "pattern" temp.txt
ミス3: IF 文の括弧内で特殊文字がエスケープされない
bat – 特殊文字のエスケープ
rem NG: & がコマンド区切りと解釈される
if "%VAR%"=="A&B" echo 一致
rem OK: ^ でエスケープする
if "%VAR%"=="A^&B" echo 一致
rem OK: 遅延展開を使う(特殊文字が安全に展開される)
if "!VAR!"=="A&B" echo 一致
ミス4: ERRORLEVEL の比較で等号を間違える
bat – ERRORLEVEL 比較の正しい書き方
rem NG: 「IF ERRORLEVEL 1」は「1以上」の意味
if ERRORLEVEL 1 echo エラー
rem OK: 明示的に比較する
if %ERRORLEVEL% neq 0 echo エラー(0以外)
if %ERRORLEVEL% equ 1 echo エラーコード1
if %ERRORLEVEL% gtr 0 echo 正の値のエラー
注意:IF ERRORLEVEL N は「ERRORLEVEL が N 以上」を意味します。特定の値と等しいかを判定するには IF %ERRORLEVEL% EQU N を使用してください。
よくある質問(FAQ)
Q: ERRORLEVEL を完全に使わないべきですか?
いいえ。ERRORLEVEL は多くのコマンドで信頼性が高く、基本的なエラー検知の第一手段として引き続き有効です。ただし、「ERRORLEVEL だけに頼る」のではなく、この記事で紹介した手法を補助的に組み合わせることで、より堅牢なエラーハンドリングが実現できます。
Q: どの手法を最初に導入すべきですか?
最も導入コストが低く効果が高いのは、|| 演算子の活用です。既存の IF ERRORLEVEL による判定を || に置き換えるだけで、コードが簡潔になり、かつエラー検知の精度が上がるケースがあります。
Q: バッチファイルが複雑になりすぎる場合は?
エラーハンドリングのロジックが複雑になってきた場合は、以下のアプローチを検討してください。
ポイント:エラーチェック用のサブルーチンを CALL :label で呼び出すことで、メインロジックの可読性を保てます。それでも複雑になる場合は、PowerShell スクリプトへの移行を検討しましょう。
Q: テスト環境でエラーハンドリングを検証するには?
意図的にエラーを発生させるテスト用バッチを作成すると効果的です。
bat – エラーハンドリングのテスト方法
rem 存在しないファイルへのアクセスでエラーを発生させる
copy nonexistent_file.txt dest.txt
rem 指定したエラーコードで終了するテスト用バッチ
rem (test_error.bat)
@exit /b 1
rem stderr に出力するテスト
echo テストエラー 1>&2
まとめ
ERRORLEVEL は便利な仕組みですが、すべてのエラーを正確に表現できるわけではありません。
実務では、以下の複数の観点を組み合わせることで、より信頼性の高いエラー検知が可能になります。
| 手法 |
概要 |
難易度 |
|| / && 演算子 |
成功/失敗で分岐を簡潔に記述 |
低 |
| stderr リダイレクト |
エラー出力の有無で判定 |
低 |
| ファイル存在チェック |
成果物の存在・サイズで検証 |
低 |
| ログ解析(FIND/FINDSTR) |
出力内容のキーワード検索 |
中 |
| FOR コマンドによるパース |
出力値の動的な取得と検証 |
中 |
| 環境変数/レジストリ |
状態変化の検出 |
中 |
| WMIC / PowerShell 連携 |
OS レベルの高度な情報取得 |
高 |
| 処理時間の計測 |
タイムアウト・異常終了の検知 |
中 |
ERRORLEVEL を「唯一の判定基準」にせず、「複数ある判断材料の一つ」として扱うことが、壊れにくいバッチ設計につながります。
まずは || 演算子の導入から始めて、プロジェクトの要件に応じてファイルチェックやログ解析などの手法を段階的に取り入れていくことをおすすめします。
バッチファイルのエラーハンドリング一覧
目的や状況別に、エラー処理の方法を整理しています。