豚吐露@wiki

for内でsetした値がforを抜けるまで反映されない

最終更新:

ohden

- view
管理者のみ編集可

for内でsetした値がforを抜けるまで反映されない

Windows XP

例えば下のようなbatを書く。
e.g.) forTest.bat
  1. @echo off
  2. echo begin: %arg%
  3. set arg=
  4. echo begin: %arg%
  5. for %%a in (%*) do (
  6. echo arg: %arg%
  7. set arg=%%a
  8. )
  9. echo exit: %arg%
引数を順番に環境変数に設定して、その環境変数を表示するというだけのスクリプト。
コマンドプロンプトを起動して、引数を『a b c』にして実行してみる。
> forTest a b c
begin:
begin:
arg:
arg:
arg:
exit: c
とまぁ、for内のechoで環境変数の値が出力できない。
2回目。引数を『a b c d e』にして実行してみる。
> forTest a b c d e
begin: c
begin:
arg:
arg:
arg:
arg:
arg:
exit: e
forを抜けた先ではちゃんと反映された環境変数を参照できる。

このようにバッチスクリプトでは妙なルールがあるみたい。
  • ()内で設定している環境変数を参照しても、()に入る前の値が使用される。
  • ()内でsetした値が反映されるのは()を抜けてから。
※つまりforに限った話ではなく『()』内でsetを使った場合に発生しうる問題。

普通...forの中でsetだけして最後にsetしたものだけを使いたいなんて状況にはなり難い。w
Microsoftも適当なもん作りやがって...(-ω-;)

で、昔のバッチスクリプトでは解決できなかったらしいんですが、今のバッチスクリプトでは仕様が追加され対応できるようになったらしい。
上のスクリプトを改善したものがコレ。
e.g.) forTest.bat
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. echo begin: %arg%
  4. set arg=
  5. echo begin: %arg%
  6. for %%a in (%*) do (
  7. echo arg: !arg!
  8. set arg=%%a
  9. )
  10. echo exit: %arg%
  11. endlocal
実行してみるとこんな感じ。
> forTest a b c d e
begin: e
begin:
arg:
arg: a
arg: b
arg: c
arg: d
exit: e
ちゃんとループの度に値が反映されてる。

ポイントは2,7,11行目。

まず2行目の『setlocal enabledelayedexpansion』。
意味としては『遅延環境変数の展開を有効にする』らしい...
要約すると『()内で設定された環境変数は()出る時(遅延して)に有効になるけど、このオプションを使うと遅延出力前に使うことができるよ。』
で、11行目はsetlocalと対になるendlocal。
一番大事な7行目。本来遅延して出力される環境変数を遅延出力前に使いたい場合は『%hoge%』ではなく『!hoge!』を使う。

要は、setlocalをファイルの頭に、endlocalをファイルのケツに書いて、setlocal~endlocal内の()内で環境変数を使う場合は『%』で無く『!』で挟む。
これで普通に()内で環境変数を使うことができる。

ちなみに...
> setlocal /?
> cmd /?
ってしても、ろくな情報出てこないんだけどね。

c.f.) setlocal /? から抜粋
ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION は、遅延環境変数の展開を有効または無効にできます。詳細については、CMD /? を参照してください。

c.f.) cmd /? から抜粋
/V:ON 区切り文字として ! を使って遅延環境変数の展開を有効にします。たとえば、 /V:ON とすると、!var! は、実行時に変数 var を展開します。var 構文は、FOR ループ中とは違い、入力時に変数を展開します。
/V:OFF 遅延環境展開を無効にします。

既定では、遅延環境変数の展開は有効ではありません。遅延環境変数の展開を有効または無効にして CMD.EXE を起動するには、/V:ON または /V:OFF スイッチを使います。コンピュータまたはログオン セッションで起動される CMD.EXE コマンドすべてに対して補完を有効または無効にするには、REGEDIT32.EXE を使ってレジストリにある次のREG_DWORD 値を設定します。

遅延環境変数の展開が有効になっている場合、感嘆符を使うと実行時に環境変数の値を置き換えることができます。

こんなんで分かるか...(-ω-;)
読んだ感じでは、cmdの起動オプションでも対応できるみたいじゃが、良く分かっとらん人が使う環境じゃったら、setlocalで対応するのがスジな気がする。



更新日: 2016年05月17日 (火) 11時17分31秒

名前:
コメント:

すべてのコメントを見る
記事メニュー
目安箱バナー