FOR文やIF文で変数の中身が消える? (DOSの変数の代入)

気になった記事が。


DOSの変数の代入 - 揮発性のメモ2

    FOR /F "tokens=1" %%i IN (hoge.txt) DO (
        ECHO NAME=%%i
        SET NAME=%%i
        ECHO NAME=%NAME%
    )
NAME=aaa
NAME=

変数に代入すると変数の中身が消える。なんだろうこれ。

http://d.hatena.ne.jp/iww/20110617/dos

>変数に代入すると変数の中身が消える。なんだろうこれ。
いえいえ、変数の中身は消えませんよ。

for文のdo以降を()でくくられていますが、この複文内の環境変数はfor文が実行される直前に、すべて環境変数の中身に展開されてから実行されます。
for文が実行されているループの都度%NAME%が参照されて変化するわけではありません。

つまり、

    FOR /F "tokens=1" %%i IN (hoge.txt) DO (
        ECHO NAME=%%i
        SET NAME=%%i
        ECHO NAME=%NAME%
    )

でFOR文実行時に環境変数NAMEが未定義(空)だった場合は

    FOR /F "tokens=1" %%i IN (hoge.txt) DO (
        ECHO NAME=%%i
        SET NAME=%%i
        ECHO NAME=          ←%NAME%の値に展開済み
    )

を実行しているのと同じです。
詳しくは、set /? で出てくるヘルプの
「最後に、遅延環境変数の展開が追加されました。」
の行以降の説明を以下に転記しますので読んでみてください。

最後に、遅延環境変数の展開が追加されました。このサポートは常に既定で
無効になっていますが、CMD.EXE の /V: のコマンド ライン スイッチを使
って有効または無効にできます。
CMD /? を参照してください。

遅延環境変数の展開は、実行時ではなく、テキスト行を読み取るときに展開
されるという現在の制限を避けるために役立ちます。
次の例は即時変数展開の問題を説明しています。

set VAR=before
if "%VAR%" == "before" (
set VAR=after;
if "%VAR%" == "after" @echo If you see this, it worked
)

この例は、論理的には IF 文が別の IF 文の本体に含まれる複合文なので、
両方の IF 文の %VAR% が、最初の IF 文を読み取ったときに展開されます。
このため、メッセージは決して表示されません。
複合文の中の IF では "before" と "after" が比較され、
決して等しくはなりません。
同様に次の例も期待したようには動作しません。

set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%

この例では、現在のディレクトリのファイルの一覧は作成されず、代わりに最後
に見つけられたファイルが LIST 変数に設定されます。
これは %LIST% が FOR 文が読み取られるとき、
一度だけ展開され、そのときは LIST 変数が空だからです。
つまり、実際に実行されている FOR ループは

for %i in (*) do set LIST= %i

で、LIST に最後に見つけられたファイルを設定し続けているだけです。

遅延環境変数の展開では、実行時に環境変数を展開するために異なった文字
(感嘆符) を使うことができます。
遅延環境変数の展開が有効な場合、上記の
例は次のように書くと意図したように動作します。

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo If you see this, it worked
)

set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%

なお!をつかった遅延変数展開は
「CMD.EXE の /V: のコマンド ライン スイッチを使って有効」
にしなくても

setlocal ENABLEDELAYEDEXPANSION

で有効にできます。詳しくは setlocal /? をご覧ください。

ようするに、FOR文の中ではSETが動作しないっぽい。なるほど。ふざけんな。

IF文とか、FOR文で使う ( ) の中だと、SETが動作しないことがある。

http://d.hatena.ne.jp/iww/20110617/dos

上記のようにSETはちゃんと動作してます。
複文はその行を実行するときに環境変数を展開するので注意が必要です。IF文とかに()を使うときも同様ですね。

というわけで、私は()をつかった複文はあえて使わないことが多いです。