【bat】バッチファイルでif ERRORLEVELが正しく判定されない理由

【bat】バッチファイルでif ERRORLEVELが正しく判定されない理由 bat

バッチファイルでエラー判定を実装するとき、if ERRORLEVEL を使っているのに分岐が意図通りに動かないという相談は非常に多いです。

原因の多くは「書き方が間違っている」のではなく、if ERRORLEVEL の判定仕様が“等号比較ではない”ことにあります。

さらに、パイプ処理や括弧ブロック、遅延展開などが絡むと、ERRORLEVEL の参照タイミングがずれて「正しく判定されていないように見える」ケースも発生します。

ここでは if ERRORLEVEL が正しく判定されない典型パターンを、仕様ベースで切り分けられるように整理します。

ERRORLEVELの基本から確認したい場合は、ERRORLEVELを使ってエラーハンドリングを行う方法も参考にしてください。

if ERRORLEVELは「以上判定」である

if ERRORLEVEL は「ERRORLEVEL が指定した数値以上なら真」という判定です。

そのため、ERRORLEVEL が 1 のときに if ERRORLEVEL 0 も真になります。

これを理解していないと、エラー時でも正常分岐が走るように見えてしまいます。

somecommand
if errorlevel 1 echo エラー
if errorlevel 0 echo 正常

このコードは ERRORLEVEL が 1 の場合、エラーも正常も両方表示されます。

if ERRORLEVEL を使う場合は、必ず大きい数値から順に判定する必要があります。

somecommand
if errorlevel 2 (
  echo 重大エラー
) else if errorlevel 1 (
  echo エラー
) else (
  echo 正常
)

「=」で比較したいなら%ERRORLEVEL%を使う

if ERRORLEVEL は仕様として等号比較ではありません。

等価や不等価を明確に書きたいなら、%ERRORLEVEL% を数値比較として使うほうが安全です。

somecommand
if %ERRORLEVEL% equ 0 (
  echo 正常
) else (
  echo エラー
)

この書き方は「0 だけを正常」と定義できるため、読み手が誤解しにくくなります。

判定対象のコマンドがズレる

ERRORLEVEL は直前に実行されたコマンドの終了コードです。

判定の前に別コマンドを挟むと、見ている ERRORLEVEL が変わります。

特に echo、dir、type、set などを挟むと、意図しないタイミングで ERRORLEVEL が更新されたり、逆に更新されず古い値が残ったりします。

判定は必ず「対象コマンドの直後」に置き、間に何も挟まない構成が安全です。

somecommand
rem ここに余計なコマンドを挟まない
if errorlevel 1 (
  echo エラー
)

パイプ処理では最後のコマンドの結果になる

パイプ(|)を使うと、ERRORLEVEL は最後に実行されたコマンドの終了コードになります。

前段のコマンドが失敗していても、後段が成功すれば ERRORLEVEL は 0 になることがあります。

逆に前段が成功でも、後段が条件不一致扱いになるコマンドだと 1 になり、失敗に見えることもあります。

type sample.txt | find "ABC" > nul
if errorlevel 1 (
  echo 見つからない(または失敗)
)

この場合は find の仕様により「見つからない」だけで 1 になります。

処理の成否判定として使うなら、パイプを分解して原因を切り分ける設計が必要です。

括弧ブロックと%ERRORLEVEL%の参照タイミング

括弧ブロック内で %ERRORLEVEL% を参照すると、パース時に値が固定されて見えることがあります。

これは ERRORLEVEL 固有の話というより、%変数%展開がブロック単位で先に評価されることが原因です。

対処としては、遅延環境変数展開を有効にし、!ERRORLEVEL! を使う方法が安全です。

@echo off
setlocal enabledelayedexpansion

somecommand
if !ERRORLEVEL! neq 0 (
  echo エラー
) else (
  echo 正常
)

ブロック内で状態を追跡するような処理では、遅延展開を前提に組む方が事故が減ります。

ERRORLEVELが更新されないコマンドがある

コマンドによっては ERRORLEVEL を更新しないものもあり、その場合は以前の値が残ります。

その結果、直前の処理が成功していても、古いエラーコードが残っているせいで「判定がおかしい」と感じることがあります。

この場合は「何のコマンドの結果を見ているか」を必ず意識し、必要なら判定前に成功が保証されているコマンドでリセットする手段もあります。

cmd /c exit 0

ただしこれは最終手段であり、基本は判定位置と構成で解決すべきです。

まとめ

if ERRORLEVEL が正しく判定されない最大の理由は、if ERRORLEVEL が“以上判定”であるという仕様を前提にしていないことです。

加えて、ERRORLEVEL が「直前コマンドの終了コード」であること、パイプでは最後のコマンドになること、括弧ブロックでは変数展開のタイミングに注意が必要なことを押さえると、原因の切り分けが一気に楽になります。

実務では、if ERRORLEVEL を使うなら大きい値から判定し、等号比較が必要なら %ERRORLEVEL% や遅延展開を使って、意図/allケースが読み取れる分岐にしておくのが最も安全です。

ERRORLEVEL関連記事