这是损坏的批处理文件:
@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%