Windowsバッチファイルで遅延環境変数展開を使用する

toc目次

バッチファイルの変数

バッチファイルの変数は、デフォルトでテキスト行が読み取られるときに展開が行われます。

通常は問題にならないのですが、FORIFで括弧を使用している場合、括弧内部の処理は全体で1行と判定されるため、括弧内でSETを使用して変数を設定してもうまく設定がされません。

@echo off
setlocal

set A=1
set B=1

if %A% == 1 (
    set B=2
    echo [IN]  A=%A%, B=%B%
)

echo [OUT] A=%A%, B=%B%

pause
[IN]  A=1, B=1
[OUT] A=1, B=2

上記コードはifの内部でB2をセットしていますが、出力結果は1となっています。

これはifの処理に入った際に括弧内も含めて変数が展開されるため、%A%%B%ともにifの前でセットしている1が展開されるためです。

if 1 == 1 (
    set B=2
    echo [IN]  A=1, B=1
)

このため、if内のechoで「[IN] A=1, B=1」と表示されます。

遅延環境変数展開

上記を回避するためには遅延環境変数展開を使用します。

遅延環境変数展開とは、バッチファイル内で変数がセットされた後、その変数が参照されるまでの間、変数の値が展開されないようにする機能です。

これにより、ループ処理などで変数の値が変わる場合でも、その都度最新の値を取得できます。

遅延環境変数展開を使用するには、バッチファイルの始めにsetlocal enabledelayedexpansionを記述し、変数名を参照する際は「%変数名%」ではなく、「!変数名!」とします。

@echo off
setlocal enabledelayedexpansion

set A=1
set B=1

if !A! == 1 (
    set B=2
    echo [IN]  A=!A!, B=!B!
)

echo [OUT] A=!A!, B=!B!

pause
[IN]  A=1, B=2
[OUT] A=1, B=2

上記は遅延環境変数展開を使っているため、括弧内でも変数の値が更新されるようになります。

遅延環境変数を使用しない方法

CALLコマンドを使用し、新しいバッチファイルのコンテキストを作ることで、遅延環境変数を使わず変数を再評価することも可能です。

CALLコマンドを使用する場合は変数は「%%変数名%%」で参照します。

@echo off
setlocal

set A=1
set B=1

if %A% == 1 (
    set B=2
    call echo [IN]  A=%%A%%, B=%%B%%
)

call echo [OUT] A=%%A%%, B=%%B%%

pause
[IN]  A=1, B=2
[OUT] A=1, B=2

CALLコマンドを使用する方法は、遅延環境変数展開と比較してパフォーマンスが劣るため、特別な理由がない限りは遅延環境変数展開を利用する方法が推奨されます。