バッチファイル内の変数展開順序

呼び出し先バッチファイルの戻り値によって次の進む先を分岐させるバッチファイルを書いているときに変数展開のタイミングということについて初めて気がついた。

必ず 0 を戻す exit0.bat と、必ず 1 を戻す exit1.bat を用意して次のようなバッチファイルを書いた。

@echo off
call exit0.bat
echo ERR = %ERRORLEVEL%
if ERRORLEVEL == 0  (
  call exit1.bat
  echo ERR = %ERRORLEVEL%
)
echo ERR = %ERRORLEVEL%

以下のような文字列の出力を期待したのだけど

ERR = 0
ERR = 1
ERR = 1

実際は以下のようになった。

ERR = 0
ERR = 0
ERR = 1

バッチファイルでは、ステートメントが実行されてから変数展開されるという流れになっていて、この例なら if のカタマリ内の実行が全部終わってから %ERRORLEVEL% に値が入るという流れ。

なにそれわかりにくい。

他のプログラム言語のように見た目通りの順序で変数展開されるようにするには、setlocal enabledelayedexpansion と定義してから !ERRORLEVEL! という名前でアクセスすれば良い。

@echo off
setlocal enabledelayedexpansion
call exit0.bat
echo ERR = %ERRORLEVEL%
if ERRORLEVEL == 0  (
  call exit1.bat
  echo ERR = !ERRORLEVEL!
)
echo ERR = %ERRORLEVEL%

そんな作法があるなんて知らなかった。