WindowsのDOS(bat)プログラムではループ内での変数インクリメントに注意が必要です

DOSでbatプログラムをちゃちゃっと作るはずだったのですが、見事にハマったのでメモ。
(未だにDOSを使わざるをえないのです。)

やりたかったこと

For文の中で変数をインクリメントしたい。


最初のコード

こんな感じで動くでしょ?

@echo off
set /A num=0
FOR %%i IN (hoge huga hugo) DO  (
    set /A num=num+1
    @echo %num%
)

出力結果は……

0
0
0

なぜ??


ポイントは「環境変数の展開タイミング」にある

batプログラムは、実行時ではなくプログラム1行ごとのの読込み時に変数を展開します。

前述のFORは、

FOR %%i IN (hoge huga hugo) DO  (
    set /A num=num+1
    @echo %num%
)

で1行です。

そのため上記のプログラムは、FOR文を読み込んだ段階で次のように変数を展開してから実行されます。

FOR %%i IN (hoge huga hugo) DO  (
    set /A num=num+1
    @echo 0
)

アンビリーバボーですね!まさかの展開ですね!
ちなみにこれを環境変数の即時展開」と呼びます。


環境変数の遅延展開」が答え!

この「即時展開」を「遅延展開」する、つまりプログラム実行時に変数を展開することで、問題を解決できます。

遅延展開を行うには2つのプロセスが必要です。

1.setlocal enabledelayedexpansion で遅延の展開を有効にする

batプログラムの冒頭で次の宣言を行います。

setlocal enabledelayedexpansion

これにより「遅延展開」を有効化します。


2.!変数!で遅延展開を実施する。

遅延展開を行う変数は、通常の変数(%変数%)と宣言方法が異なります。
「%」ではなく「!」(エクスクラメーション)で変数を囲むことで、「その変数は遅延展開するんだよ」という意味合いになります。


最終的なコード

@echo off
setlocal enabledelayedexpansion

set /A num=0
FOR %%i IN (hoge huga hugo) DO  (
    set /A num=num+1
    @echo !num!
)

出力結果は……

1
2
3

やりました!!


参考にさせていただきました

こちらのサイトに詳しく紹介されています。
http://wa3.i-3-i.info/word12518.html


[改訂新版]Windowsコマンドプロンプトポケットリファレンス

[改訂新版]Windowsコマンドプロンプトポケットリファレンス

Windows DOS/コマンドプロンプト辞典

Windows DOS/コマンドプロンプト辞典