Windows 7 批处理文件:为什么这些 IF 块阻止我将变量分配给值?

Windows 7 批处理文件:为什么这些 IF 块阻止我将变量分配给值?

这是损坏的批处理文件:

@echo off
if prod==prod (
    if xps==xps (
        set i1=prodxpsi1
        set i2=prodxpsi2
        set e1=prodxpse1
        set e2=prodxpse2
    ) else (
        set i1=prodzpsi1
        set i2=prodzpsi2
        set e1=prodzpse1
        set e2=prodzpse2
    )

    if 1==1 (
        echo %i1%, %i2%, %e1%, %e2%
    ) else (
        echo %i1%, %i2%, %e1%, %e2%
    )
)

pause

if prod==prod但是,当我像这样取出外部块时,它就可以工作了:

@echo off
if xps==xps (
    set i1=prodxpsi1
    set i2=prodxpsi2
    set e1=prodxpse1
    set e2=prodxpse2
) else (
    set i1=prodzpsi1
    set i2=prodzpsi2
    set e1=prodzpse1
    set e2=prodzpse2
)

if 1==1 (
    echo %i1%, %i2%, %e1%, %e2%
) else (
    echo %i1%, %i2%, %e1%, %e2%
)

pause

当我第一次运行批处理文件时,它回显, , ,。当我第二次运行它时,它工作正常:

哈哈乌特

答案1

这是命令解析器的一个怪癖。由于括号的存在,它将从if ...到 的所有内容视为)一行。当它读取这一“一行”时,它会先将所有变量扩展为其值,然后再处理其中的任何内容。set所有命令都在变量扩展后发生。

解决方案有两种:分支和延迟扩展。

分支机构:确保set命令和echo命令不在同一组最上面的括号中:

@echo off
if not prod==prod goto :end
if xps==xps (
    set ...
) else (
    set ...
)
if 1==1 (
    ...
)
:end
pause

延迟扩展:这会导致变量根据需要扩展,而不是提前扩展。使用命令SetLocal EnableDelayedExpansion激活此模式,使用 ! 标记以这种方式引用变量,EndLocal完成后使用命令。请注意,EndLocal将忘记在 之后声明的任何变量SetLocal,因此您可能需要在命令SetLocal之后移动到set

@echo off
setlocal enabledelayedexpansion
if prod==prod (
    if xps==xps (
        set i1=prodxpsi1
        ...
    ) else (
        set i1=prodzpsi1
        ...
    )
    if 1==1 (
        echo !i1!, !i2!, !e1!, !e2!
    ) else (
        echo !i1!, !i2!, !e1!, !e2!
    )
)
endlocal
pause

答案2

如果您启用延迟扩展,然后使用!var!语法引用变量,那么它就会按预期运行。

@echo off

setlocal enabledelayedexpansion

if prod==prod (
    if xps==xps (
        set i1=prodxpsi1
        set i2=prodxpsi2
        set e1=prodxpse1
        set e2=prodxpse2
    ) else (
        set i1=prodzpsi1
        set i2=prodzpsi2
        set e1=prodzpse1
        set e2=prodzpse2
    )

    if 1==1 (
        echo !i1!, !i2!, !e1!, !e2!
    ) else (
        echo !i1!, !i2!, !e1!, !e2!
    )
)

帮助文本(cmd /?)解释道:

/V:ON - 使用 ! 作为分隔符启用延迟环境变量扩展。例如,/V:ON 将允许 !var! 在执行时扩展变量 var。var 语法在输入时扩展变量,这与 FOR 循环内部的情况完全不同。

如果启用了延迟环境变量扩展,则可以使用感叹号在执行时替换环境变量的值。

当您将整个内容包装在一个if语句中时,整个块本质上就变成了一个命令。命令中的所有变量都在输入时展开,即在部分if xps==xps开始之前。在脚本中的这一点上,变量尚未定义。

通过使用!var!语法和延迟扩展,!i1!直到执行该特定行时才会评估的值。

谢谢!你的问题让我今天学到了很多东西!

答案3

整个cmd过程更像是一个预处理器,它运行一次文件,创建预处理的“真实”文件,然后运行该文件。这意味着cmd不会(重新)评估动态设置的变量(或for基于“变量”内容变化的循环)。

因为您对问题的描述有点模糊:您是否使用变量而不是字符串“prod”和“xps”并期望动态解释这些“变量”?

答案4

以下程序简单说明了如何在 IF 语句中使用同一语句中设置的变量的值。它们旨在显示“aa”(如果未输入参数)和“bb”(如果输入参数)。示例 1 在没有参数的情况下仅显示“a”。其他示例均可正常工作。

在我看来,使用“goto”的示例 2 是最直接、最容易理解的。

示例 2、5 和 6 通过从 IF 语句中删除有问题的代码来实现。

@echo off & setlocal enableextensions & :: 1
if "%1" equ "" (
   set x=a
   set y=%x%
   ) else (
   set x=b
   set y=b
   )
echo %x%%y%

@echo off & setlocal enableextensions & :: 2
if "%1" neq "" goto 10
   set x=a
   set y=%x%
   goto 20
   :10
   set x=b
   set y=b
:20
echo %x%%y%

@echo off & setlocal enableextensions & :: 3
setlocal enabledelayedexpansion
if "%1" equ "" (
   set x=a
   set y=!x!
   ) else (
   set x=b
   set y=b
   )
endlocal & set "x=%x%" & set "y=%y%"
echo %x%%y%

在以下示例中,请注意“else”块中的“setlocal”。这与“endlocal”匹配,因此变量“x”和“y”保持为程序的本地变量。(否则,“endlocal”会取消第一行中的“setlocal”。)

@echo off & setlocal enableextensions & :: 4
if "%1" equ "" (
   set x=a
   setlocal enabledelayedexpansion
   set y=!x!
   ) else (
   set x=b
   set y=b
   setlocal
   )
endlocal &  set "y=%y%"
echo %x%%y%

@echo off & setlocal enableextensions & :: 5
if "%1" equ "" (
   call :seta
   ) else (
   call :setb
   )
echo %x%%y%
exit /b
:seta
   set x=a
   set y=%x%
   exit/b
:setb
   set x=b
   set y=b
   exit/b

@echo off & setlocal enableextensions & :: 6
set x=a
if "%1" equ "" (
   set y=%x%
   ) else (
   set x=b
   set y=b
   )
echo %x%%y%

相关内容