バッチもん研究所 blog このページをアンテナに追加 RSSフィード Twitter

2011-07-09

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

気になった記事が。


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


    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文とかに()を使うときも同様ですね。

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

iwwiww 2011/07/09 12:53 ありがとうございます。あやふやだったところの理解が深まりました。
当時、遅延展開はなんかの理由で使えなくて、仕方なく普通にGOTOで飛んで単文で処理してました。

remrem 2011/07/19 22:39 コマンドプロンプトでちょっと行き詰まってるので何か解決のヒントでもあればお教えください
■やりたいのは環境変数にコマンドをsetして実行されるやり方です
このような方法は通常のやり方ではなくかなりイレギュラーだとは分かってますが
でも実際に動くし便利なので自分は多用しています。ただFOR文が曲者でこれがなかなかうまくいきません
例文)
@echo off
setlocal enabledelayedexpansion

for /L %%i in (1,1,2) do echo=%%i

set C=rem
!C! for /L %%i in (3,1,4) do echo=%%i

set C=
!C! (for /L %%i in (5,1,6) do echo=%%i)

set C=
!C!(for /L %%i in (7,1,8) do echo=%%i)

set C=for
!C! /L %%i in (9,1,10) do echo=%%i

set C=rem
!C! echo=エコー1

set C=
!C!echo=エコー2

set C=echo
!C!=エコー3

set C=call
!C! echo=エコー4

set C=ec
set C2=ho
!C!!C2!=エコー5

set C=dir
!C!

set c2=/B
!C! !c2!

set C=rem
!C! !c2!

pause

echo文をremでコメントアウトして実行させない
その逆で、実行されるやりかた
これの切り替えはうまくいきます
エコー1はコメント文になって実行されない
エコー2〜5は実行されます

for文だとremでコメント文にするのはできるが
実行させる方がさっぱり

echo以外でもdir call goto他多数で可能だが煩雑になるので少数の例にとどめました

ともかくfor文ができたらいいんですが、、、

jak-sanjak-san 2011/07/21 17:27 iwwさん
記事にさせて頂きました。お役に立てましたでしょうか。

remさん
わざわざ環境変数の遅延展開を使われていますが、そのような場合は通常の環境変数の展開で良いはずです。
!C!→%C%
!C2!→%C2%
試しにやってみたら、for文を含めて動作しましたよ。

remrem 2011/07/23 21:36 うっ動きますね。ただ言い訳すると上記のは例文であって実際に作ってるbatファイルではsetlocal enabledelayedexpansion下で書くのが習慣のようになっているのできがつきませんでした。ということは遅延展開と即時展開を使い分けて、、、これはこれでbatファイル初心者には新たな問題が、、とにかく、ご回答ありがとうございました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/jak-san/20110709/1310168663