2011-12-12 13 views
14

如果我採取以下Windows批處理代碼段,運行它:(Windows批處理)轉到內,如果塊的行爲非常奇怪

echo foo 
if 1 == 1 (
    echo bar 
    goto asdf 
    :asdf 
    echo baz 
) else (
    echo quux 
)

我所期望的輸出是:

foo 
bar 
baz

但是相反我得到:

foo 
bar 
baz 
quux

如果我註釋掉goto asdf行,它會給出我期望的輸出。 echo quux行應該永遠不會被執行,那麼爲什麼goto會導致這種情況發生?

UPDATE:對於它的價值,這裏有一個變通方法,正確地做什麼,我本來打算:

goto BEGIN 

:doit 
    echo bar 
    goto asdf 
    :asdf 
    echo baz 
    goto :EOF 

:BEGIN 

echo foo 
if 1 == 1 (
    call :doit 
) else (
    echo quux 
)

然而,這並沒有回答我原來的問題。

回答

23

CALL或GOTO的目標不應在括號內的塊語句內。它可以完成,但正如你所看到的,結果可能不會成爲你想要的。

整個IF(...)ELSE(...)構造被解析,並在處理它之前被加載到內存中。換句話說,它在邏輯上被視爲一行代碼。分析完成後,CMD.EXE期望在IF/ELSE構造之後的下一行開始繼續解析。

解析階段後,複合命令從內存中執行。 IF子句被正確處理並且ELSE子句被正確跳過。但是,在IF(true)子句中,執行GOTO :asdf,所以CMD.EXE正式開始掃描標籤。它從IF/ELSE結尾開始,掃描到文件底部,循環回到頂部,並掃描直到找到標籤。該標籤恰好在您的IF條款中,但標籤掃描器對此詳細信息一無所知。因此,當複雜命令從內存執行結束時,批處理將從標籤恢復,而不是從複雜的IF/ELSE結束。

所以在這一點批量處理器看到並執行接下來的幾行

echo baz 
) else (
    echo quux 
) 

巴茲相呼應,所以是QUUX。但你可能會問:「爲什麼不) else (和/或)生成語法錯誤,因爲他們現在是不平衡的,不再解析爲較大的IF語句的一部分?

那是因爲如何處理)

如果有一個開放的(當遇到),那麼)處理你所期望的。

但積極的,如果解析器期待一個命令,發現一個)時,沒有主動打開(,然後)將被忽略,並且該行其餘部分的所有字符都將被忽略!現在)現在作爲REM語句發揮作用。

+2

+1好解釋,戴夫。較短的一個是:當執行GOTO時,任何* active *'FOR/IF'命令被取消,並且批處理文件從標籤開始繼續。 – Aacini

5

一組括號內的任何內容都被認爲是一行,處理,解釋和執行一擊。您的腳本達到goto asdf並跳出該塊/行。標籤:asdf沒有支架,所以它開始逐行閱讀線條。它達到else,但:asdfelse之間沒有if,所以它忽略它。

爲了避免這樣的問題,我總是在iffor聲明中使用gotocall而不是塊。這樣可以進一步排除goto語句中的問題,並且還會整理出很多與變量有關的問題。

要使用goto

echo foo 
if 1 == 1 goto bar 
echo quux 
goto nextbit 

:bar 
echo bar 
goto asdf 
:asdf 
echo baz 

:nextbit 
:: more script... 

或者用call

echo foo 
if 1 == 1 (call :bar) else (call :quux) 
:: more script... 
exit /b 

:bar 
echo bar 
goto asdf 
:asdf 
echo baz 
exit /b 

:quux 
echo quux 
exit /b 
0

在這種情況下,它原來可能與循環不佳嵌套和結構的IF語句中,如上面的https://stackoverflow.com/users/1012053/dbenham所清楚解釋的那樣。

這麼說,我想通了另一個類似的考慮,由於這個問題... 請看看一個下列問題,並隨後這裏的解決方案: ". was unexpected at this time" generated from batch script line 'if exist [file] (...

的解決方案是簡單的「(」治療和')'在IF語句塊內的ECHO行上。

問題是,在排除IF(和可能的FOR)語句故障時,請考慮將特殊字符作爲可能的問題來源。

HTH