18

私は単純な .bat ファイルを書いていますが、奇妙な動作に遭遇しました。簡単な if/else を実行する必要がある場所がいくつかありますが、ブロック内のコードが正しく機能していないようです。

エラーを示す簡単なケースを次に示します。

@echo off

set MODE=FOOBAR

if "%~1"=="" (
  set MODE=all
  echo mode: %MODE%
) else (
  set MODE=%~1
  echo mode: %MODE%
)
echo mode: %MODE%

私が得ている出力は次のとおりです。

C:\>test.bat test
mode: FOOBAR
mode: test

コード ブロック内のエコーが変数の新しい値を取得しないのはなぜですか? 私が書いている実際のコードでは、いくつかの変数を作成し、if/else のスコープ内でそれらを参照する必要があります。これを切り替えて、if/else の代わりにラベルと goto を使用することもできますが、それはそれほどきれいではないようです。

この動作の原因は何ですか? コード ブロック内の変数に何らかの制限はありますか?

4

4 に答える 4

26

cmd の静的変数展開の問題が発生しています。MODE 変数は一度だけ評価されます。@echo off 行を省略すると、これを確認できます。

セットから /? ドキュメンテーション:

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

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

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

両方の IF ステートメントの %VAR% は、複合ステートメントである IF の本体を論理的に含むため、最初の IF ステートメントが読み取られるときに置換されるため、メッセージは表示されません。したがって、複合ステートメント内の IF は、実際には「前」と「後」を比較していますが、これは決して等しくなりません。同様に、次の例は期待どおりに動作しません。

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

現在のディレクトリにファイルのリストを作成するのではなく、代わりに LIST 変数を最後に見つかったファイルに設定するという点で。繰り返しますが、これは、FOR ステートメントが読み取られるときに %LIST% が 1 回だけ展開され、その時点で 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%
于 2008-11-20T15:26:58.077 に答える
2

setlocal EnableDelayedExpansion

/v フラグを有効にします

于 2009-09-10T19:01:55.217 に答える
-2

読み取りと書き込みで異なるスコープ規則を使用しているようです。

この線を消すと

set MODE=FOOBAR

期待どおりに動作します。そのため、if/else で必要に応じて変数を設定するには、おそらく複雑なシリーズが必要になるでしょう。

于 2008-11-20T15:30:09.787 に答える