(Windows批处理)转到if块表现得非常奇怪

col*_*dot 18 goto batch-file

如果我使用以下Windows批处理代码段并运行它:

echo foo
if 1 == 1 (
    echo bar
    goto asdf
    :asdf
    echo baz
) else (
    echo quux
)
Run Code Online (Sandbox Code Playgroud)

我期望的输出是:

foo
bar
baz
Run Code Online (Sandbox Code Playgroud)

但相反,我得到:

foo
bar
baz
quux
Run Code Online (Sandbox Code Playgroud)

如果我注释掉该goto asdf行,它会给出我期望的输出.这echo quux条线永远不应该被排除,那么为什么goto的存在会导致这种情况发生呢?

更新:对于它的价值,这里是一个正确执行我最初预期的解决方法:

goto BEGIN

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

:BEGIN

echo foo
if 1 == 1 (
    call :doit
) else (
    echo quux
)
Run Code Online (Sandbox Code Playgroud)

但是,这不符合我原来的问题.

dbe*_*ham 29

CALL或GOTO的目标永远不应该在括号内的块语句内.它可以完成,但正如你所看到的,结果可能不是你想要的.

整个IF(...)ELSE(...)构造在处理任何构造之前被解析并加载到内存中.换句话说,它在逻辑上被视为一行代码.在解析之后,CMD.EXE期望从IF/ELSE构造之后的下一行开始继续解析.

在解析阶段之后,从内存执行复杂命令.正确处理IF子句并正确跳过ELSE子句.但是在IF(true)子句中,你执行a GOTO :asdf,所以CMD.EXE开始扫描标签.它从IF/ELSE结束开始,扫描到文件的底部,循环回到顶部,然后扫描直到找到标签.标签恰好在您的IF子句中,但标签扫描器对该细节一无所知.因此,当复杂命令从内存完成执行时,批处理将从标签而不是从复杂IF/ELSE的末尾恢复.

因此,此时批处理器会查看并执行接下来的几行

    echo baz
) else (
    echo quux
)
Run Code Online (Sandbox Code Playgroud)

baz得到了回应,quux也是如此.但是你可能会问,"为什么不) else (和/或)生成语法错误,因为它们现在是不平衡的,不再被解析为更大的IF语句的一部分?

那是因为如何)处理.

如果遇到打开的(活动状态),则会按)预期处理.

但是如果解析器期望一个命令并找到一个)没有活动打开的时间(,那么)它将被忽略,并且该行的其余部分上的所有字符都将被忽略!实际上,)现在作为REM声明起作用.

  • +1好解释,戴夫.较短的一个是:当执行`GOTO`时,任何*active*`FOR/IF`命令被取消,批处理文件从标签继续. (3认同)

Han*_*ood 7

括号内的任何内容都被视为单行,在一次点击中处理,解释和执行.您的脚本到达goto asdf并跳出该块/行.在标签处:asdf,没有支架,因此它开始逐个读取线条.它达到了else,但没有if介于两者之间:asdf,else所以它忽略了它.

为了防止这样的问题,我总是使用gotocallon iffor语句,而不是块.这可以用进一步的goto陈述来解决问题,也可以用变量来解决很多问题.

使用goto:

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

:bar
echo bar
goto asdf
:asdf
echo baz

:nextbit
:: more script...
Run Code Online (Sandbox Code Playgroud)

或者使用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
Run Code Online (Sandbox Code Playgroud)