2011-12-12 17 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