0

有两个文件,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”两次。奇怪的?漏洞?

4

2 回答 2

4

我认为这两个脚本中的任何一个都不会像您认为的那样做(具体来说,第二个脚本之所以这样工作,只是因为您在第一个脚本之后运行它)。

为什么它会这样?

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

为了显示:

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%

这只是一个命令,因此变量在运行之前只扩展一次(特别是,它们不会在 the 之后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

通过分解命令块来解决问题

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

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
于 2012-07-05T10:29:41.373 回答
2

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

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

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

于 2012-07-05T10:20:51.733 回答