'for'语句在Windows批处理(.bat)文件中包含在'if'语句中时很奇怪

时间:2012-07-05 10:13:03

标签: windows batch-file

有两个文件,output.txt和test.bat。在output.txt中,只有一行'1 2 3 4',test.bat的内容是

@echo off
set condition=1

if "%condition%" == "1" (
    for /F "tokens=1,2,3,4* delims= " %%a in (output.txt) do set variable=%%a
    echo %variable%
)
pause

运行test.bat什么都不回应。但是,如果我稍微改变一下,请注意以下几点:

@echo off
set condition=1
for /F "tokens=1,2,3,4* delims= " %%a in (output.txt) do set variable=%%a
echo %variable%
if "%condition%" == "1" (
    for /F "tokens=1,2,3,4* delims= " %%a in (output.txt) do set variable=%%a
    echo %variable%
)
pause

它会两次回复'1'。 奇怪的?错误?

2 个答案:

答案 0 :(得分:4)

我认为这两个脚本中的任何一个都不符合你的想法(具体来说,第二个脚本的工作原理只是因为你在第一个脚本之后运行它)。

为什么它会像那样?

需要注意的重要一点是,除非打开延迟变量扩展,否则将在执行该命令之前为每个命令计算变量。这在使用IF时尤其重要,因为 parens中的整个命令块被视为一个命令

举例说明:

SET foo=
ECHO foo = %foo%
IF 1==1 (
    SET foo=bar
    ECHO foo = %foo%
)

上面的脚本将输出:

foo =
foo =

原因是条件相当于

IF 1==1 SET foo=bar && ECHO foo = %foo%

这只是一个命令,因此变量在运行之前只展开一次(特别是,它们不会在SET之后和ECHO之前展开。

这也是您的脚本中发生的事情。由于%variable%设置为并且在块内回显,ECHO实际上对输入块之前的值%variable%进行操作,因此您看不到“当前”的价值。

如何解决?

解决延迟变量扩展的问题

有两种方法可以解决这个问题。直截了当的是使用SETLOCAL启用延迟变量扩展,然后使用语法!var!而不是%var%引用变量:

SETLOCAL ENABLEDELAYEDEXPANSION
SET foo=
ECHO foo = !foo!
IF 1==1 (
    SET foo=bar
    ECHO foo = !foo!
)

上面的脚本将输出:

foo =
foo = bar

通过分解命令块解决问题

还有另一种方法可以做到这一点:记住在执行每个命令(或块)之前扩展一次变量。因此,如果您希望在SETECHO之间进行扩展,则可以通过分解块来实现扩展。您可以通过使用NOT反转测试并使用GOTO跳过“成功”分支中较早的代码来执行此操作:

SET foo=
ECHO foo = %foo%
IF NOT 1==1 GOTO :proceed
SET foo=bar
ECHO foo = %foo%
:proceed

以上脚本也会输出:

foo =
foo = bar

将解决方案应用于您的示例

这两种方法都转化为:

SETLOCAL ENABLEDELAYEDEXPANSION
IF "%condition%" == "1" (
    FOR /F "tokens=1,2,3,4* delims= " %%a in (output.txt) DO SET variable=%%a
    ECHO !variable!
)

或者这个:

IF NOT "%condition%" == "1" GOTO :proceed
FOR /F "tokens=1,2,3,4* delims= " %%a in (output.txt) DO SET variable=%%a
ECHO %variable%
:proceed

答案 1 :(得分:2)

不是错误 - 这是CMD如何实现变量扩展的副作用。 Raymond Chen在这里有一篇文章:

  

http://blogs.msdn.com/b/oldnewthing/archive/2006/08/23/714650.aspx

具体来说,在您的第一个示例中,从展开%variable%开始,它从未被设置过。