Chapter 05

ブロック IF / FOR と環境変数の遅延展開

zetamatta
zetamatta
2020.11.17に更新

IF や FOR は1行で書いてしまわないといけないかというと、現代ではそうでもなく、丸括弧を使うと複数行に展開して書くことができます。

if "%1" == "a" (
    echo arg is a
    echo aaaaaaaa
) else (
    echo arg is else
    echo bbbbbbbb
)

ただし、環境変数の展開のタイミングが IF や FOR の実行前 なので、注意が必要です。たとえば、次のバッチファイルは X に 1 2 3 4 5 という値をセットしたいのですが、期待どおり動作しません。

@echo off
setlocal

set X=1
for %%I in (2 3 4 5) do (
    set "X=%X% %%I"
)
echo X=%X%

endlocal
exit /b
$ f
X=1 5

ENABLEDELAYEDEXPANSION による遅延展開

回避策として SETLOCAL ENABLEDELAYEDEXPANSION を使えば「!環境変数名!」形式の環境変数の遅延展開が使えます。

@echo off
setlocal

SETLOCAL ENABLEDELAYEDEXPANSION
set X=1
for %%I in (2 3 4 5) do (
    set "X=!X! %%I"
)
echo X=%X%

endlocal
exit /b
$ f
X=1 2 3 4 5

サブルーチン分離による遅延展開

問題点としては

  • ENABLEDELAYEDEXPANSION の綴りが覚えにくい(お前だけや!)
  • SETLOCAL ENABLEDELAYEDEXPANSION を実行する前の CMD の遅延評価モードが有効だったか、無効だったか分からないので、まったく副作用がないバッチファイルというわけではなくなってしまう

という点があります。そのため、筆者個人としては、下記のようにサブルーチンに分けてしまう方が好みです。

@echo off
setlocal

set X=1
for %%I in (2 3 4 5) do call :setx %%I
echo X=%X%

endlocal
exit /b

:setx
    set "X=%X% %1"
    exit /b
$ f
X=1 2 3 4 5

call set という遅延展開

call /?set /? の説明にないため原理はよくわかっていないのですが、set の前に call を置くことで、展開を2回させることができるようです。

@echo off
setlocal

set X=1
for %%I in (2 3 4 5) do (
    call set X=%%X%% %%I
)
echo X=%X%

endlocal
exit /b
$ f
X=1 2 3 4 5

call dir などという使い方もできるため、call には eval 的な機能があるのかもしれません。