2008-11-20 58 views
17

我正在寫一個簡單的.bat文件,我遇到了一些奇怪的行爲。有幾個地方我必須做一個簡單的if/else,但塊內部的代碼似乎不能正常工作。.bat文件中奇怪的範圍問題

以下是一個演示該錯誤的簡單情況:

@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的範圍內引用它們。我可以切換使用標籤和gotos而不是if/else,但這看起來並不那麼幹淨。

是什麼導致了這種行爲?代碼塊中的變量是否存在某種限制?

回答

26

您遇到了cmd的靜態變量擴展問題。 MODE變量僅被評估一次。如果你省略@echo off行,你可以看到這個。

從設置/?文檔:

Finally, support for delayed environment variable expansion has been added. This support is always disabled by default, but may be enabled/disabled via the /V command line switch to CMD.EXE. See CMD /?

Delayed environment variable expansion is useful for getting around the limitations of the current expansion which happens when a line of text is read, not when it is executed. The following example demonstrates the problem with immediate variable expansion:

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

would never display the message, since the %VAR% in BOTH IF statements is substituted when the first IF statement is read, since it logically includes the body of the IF, which is a compound statement. So the IF inside the compound statement is really comparing "before" with "after" which will never be equal. Similarly, the following example will not work as expected:

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

in that it will NOT build up a list of files in the current directory, but instead will just set the LIST variable to the last file found. Again, this is because the %LIST% is expanded just once when the FOR statement is read, and at that time the LIST variable is empty. So the actual FOR loop we are executing is:

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

which just keeps setting LIST to the last file found.

Delayed environment variable expansion allows you to use a different character (the exclamation mark) to expand environment variables at execution time. If delayed variable expansion is enabled, the above examples could be written as follows to work as intended:

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% 
+0

有沒有辦法在一個bat文件的頂部編程設置標誌?大多數情況下,這將由另一個bat文件運行,並且其運行環境不固定(可能會雙擊,可能從cygwin shell運行,可能來自cmd等)。 – Herms 2008-11-20 15:37:51

+3

@Herms,「setlocal enabledelayedexpansion」開頭,「endlocal」結尾。 – paxdiablo 2008-12-15 11:45:44

-2

看起來像讀取和寫入使用不同的範圍規則。

如果消除此行

set MODE=FOOBAR 

它會正常工作。因此,如果/ else按照需要填充變量,則可能需要複雜的系列。

2

SETLOCAL EnableDelayedExpansion

將使/ V標誌