バッチスクリプトの for /R 内で変数を書き替えながら処理をする
if やfor 内での変数展開は、通常の変数展開とは書式が異なるらしい。
下記のような処理は失敗します。
@echo off setlocal SET imagedir=%USERPROFILE%\My Documents\My Pictures SET /A count=0 for /R "%imagedir%" %%f in ("*.bmp") do ( set /A count=count+1 echo カウント: %count% ) echo 合計: %count%
カウント: 0 カウント: 0 カウント: 0 カウント: 0 カウント: 0 合計: 5
期待通りの動作を得るには、下記のようにする。
@echo off setlocal ENABLEDELAYEDEXPANSION SET imagedir=%USERPROFILE%\My Documents\My Pictures SET /A count=0 for /R "%imagedir%" %%f in ("*.bmp") do ( set /A count=count+1 echo カウント: !count! ) echo 合計: %count%
カウント: 1 カウント: 2 カウント: 3 カウント: 4 カウント: 5 合計: 5
環境変数の他に遅延環境変数というのがあるらしく、!count! のように % ではなく ! を利用する。
詳しい理由はマニュアル(あるのかどうか知らん)か他サイトを見て欲しい。
ちなみに、以下のような書き方は、分かりにくいが、やはりダメ。
>test.bat & if %ERRORLEVEL% neq 0 echo fail>test.bat & echo %ERRORLEVEL%なぜなら、環境変数はその行全体の実行開始前に展開されるから。
つまり実行前に%ERRORLEVEL%の値が0だったとすると、以下のように展開されてから実行されることになる。>test.bat & if 0 neq 0 echo fail>test.bat & echo 0したがって、test.batがどんな値を返して(%ERRORLEVEL%にセットされて)も、実行前の値で「&」以降が処理されるということになる。
(この為に、エラーコードチェックに関してはERRORLEVELという演算子がわざわざ用意されているわけだ。これなら値の取得自体はif文の実行時に行うわけだから。)
これはその他の環境変数を使う場面でも同様に起こり得る。特にif文やfor文ではよく勘違いしてやってしまう。「help for」「for /?」でもわざわざ例示されているくらいだ。>set var=aaa >set var=bbb & echo %var% →「set var=bbb & echo aaa」と展開されてから実行される aaa >echo %var% bbb →「set var=bbb」が実行されなかったわけではないので、後から見ればちゃんとセットされているset var=1 if %var% == 1 ( →「if 1 == 1 ( set/a var=%var% + 2 set/a var=1 + 2 echo %var% echo 1 ) )」と展開されてから実行されるset list= for %%i in (aa bb) do @set list=%list%;%%i →「for %i in (aa bb) do @set list=;%i」と展開されてから処理されるので、 echo %list% 「@set list=;aa」「@set list=;bb」が実行される
なお、この問題を解消する為に遅延環境変数というものもある(遅延環境変数を有効にしないと使えないが)。
>set var=123 >set var=234 & echo 通常:%var% 遅延:!var! 通常:123 遅延:!var! >cmd /v:on …遅延環境変数を有効にして新しいコマンドプロンプトを起動 >set var=123 >set var=234 & echo 通常:%var% 遅延:!var! 通常:123 遅延:234 >exit …元のコマンドプロンプトへ戻る