WindowsのFor文でコマンドの結果値が固定されてしまう


バッチファイル界の魔境と呼ばれているらしい「遅延環境変数」に惑わされたおはなし。

語源→バッチファイル界の魔境『遅延環境変数』に挑む(おまけもあるよ)

%hoge% じゃなくて !hoge! を使え

結論からいうとfor文内のコマンドが実行されるタイミングは、実行の瞬間ではなくてfor文が開始されるタイミングらしい。
そのためfor文内でコマンドの結果を変化させたい(最新の値を表示したい)場合はつぎのように遅延環境変数を使って、コーディングする必要がある。

delayedexpansion.bat
@echo off
setlocal enabledelayedexpansion

for /L %%i in (1,1,10) do (
  echo %%i
    echo !time!
    timeout 5
)

ポイントは2点

  • 冒頭にsetlocal enabledelayedexpansionを宣言
  • コマンドを%%じゃなくて、!!で囲む

コマンドの値が変化しない?

はじめに以下のコードを実行したところ、timeコマンドの値が一切変化しない事象に遭遇した

normal.bat
@echo off

for /L %%i in (1,1,10) do (
    echo %%i
    echo %time%
    timeout 5
)

Windowsのバッチファイルを書いたことはなかったので書きっぷりがイケてないのかなと思い、for文をばらしてfor文内のコードを10個コピペしたら普通に動いたので、コードは想定通りに動きそう(少なくともfor文を使わなければ)

となるとfor文が原因っぽいけど、思い当たる節がない。

しかたないので、Google大先生にお伺いしたところ、答えとなりそうな記事を発見

batファイルでfor文内に変数を利用する場合の罠

さらに調査を続けると、
結論としては、遅延環境変数が問題であることが分かりました。

どうやら、Windowsのバッチファイルで、
括弧()をつかったブロック文を作った場合、
括弧に入った瞬間に内部の環境変数が展開されるようです。

記事は変数のことのようだが内容的にコマンドも同じことが適用されそう。
ということで、最初のコードに書き換えたところ無事動いた。