我有两个非常简单的批处理文件示例:
给变量赋值:
@echo off
set FOO=1
echo FOO: %FOO%
pause
echo on
正如预期,结果如下:
FOO: 1
Press any key to continue . . .
但是,如果我将相同的两行放在 IF NOT DEFINED 块内:
@echo off
IF NOT DEFINED BAR (
set FOO=1
echo FOO: %FOO%
)
pause
echo on
这不料结果是:
FOO:
Press any key to continue . . .
这不应该与 IF 有任何关系,显然该块正在执行。如果我在 if 上方定义 BAR,则只会显示来自 PAUSE 命令的文本,正如预期的那样。
是什么赋予了?
后续问题:有没有办法可以在不使用 setlocal 的情况下启用延迟扩展?
如果我从另一个文件内部调用这个简单的示例批处理文件,则该示例将设置 FOO,但仅限于本地。
例如:
测试调用程序
@call test.bat
@echo FOO: %FOO%
@pause
测试脚本
@setlocal EnableDelayedExpansion
@IF NOT DEFINED BAR (
@set FOO=1
@echo FOO: !FOO!
)
将显示:
FOO: 1
FOO:
Press any key to continue . . .
这样的话,看来我必须在CALLER中启用延迟扩展,这可能会很麻烦。
答案1
批处理文件中的环境变量在解析一行时会展开。对于用括号分隔的块(如您的if not defined
),整个块算作一个“行”或命令。
这意味着所有出现的 %FOO% 都被其值替换前块已运行。 在你的情况下没有任何内容,因为变量还没有值。
为了解决这个问题,你可以启用延迟扩张:
setlocal enabledelayedexpansion
延迟扩展会导致在执行时对感叹号(!
)分隔的变量进行评估而不是解析,这将确保您的情况的正确行为:
if not defined BAR (
set FOO=1
echo Foo: !FOO!
)
help set
详细说明如下:
最后,添加了对延迟环境变量扩展的支持。默认情况下,此支持始终处于禁用状态,但可以通过
/V
命令行开关启用/禁用CMD.EXE
。请参阅CMD /?
延迟环境变量扩展对于解决当前扩展的限制很有用,这种限制发生在读取一行文本时,而不是执行时。以下示例演示了立即变量扩展的问题:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
永远不会显示该消息,
%VAR%
因为两个都IF
当读取第一个 IF 语句时,语句会被替换,因为它在逻辑上包括 的主体IF
,而 是复合语句。因此,复合语句中的 IF 实际上是在比较“之前”与“之后”,两者永远不会相等。同样,以下示例也不会按预期工作:set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
因为它将不是建立当前目录中的文件列表,但只会将变量设置
LIST
为找到的最后一个文件。同样,这是因为%LIST%
在读取语句时只扩展一次FOR
,而那时变量LIST
为空。所以我们执行的实际 FOR 循环是:for %i in (*) do set LIST= %i
它只是继续设置
LIST
为最后找到的文件。延迟环境变量扩展允许您使用不同的字符(感叹号)在执行时扩展环境变量。如果启用了延迟变量扩展,则上述示例可以按如下方式编写以按预期工作:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "!VAR!" == "after" @echo If you see this, it worked ) set LIST= for %i in (*) do set LIST=!LIST! %i echo %LIST%
答案2
当命令位于一行(&
是命令分隔符)时,也会发生相同的行为:
if not defined BAR set FOO=1& echo FOO: %FOO%
我最喜欢的是 Joey 的解释。但请注意,这enabledelayedexpansion
在 Windows NT 4.0 上不起作用(我不确定 Windows 2000 是否也起作用)。
关于您的后续问题,不,没有 就不可能EnableDelayedExpansion
。setlocal
但是,最初与您相反的行为可用于解决第二个问题:诀窍是endlocal
在同一行上再次设置所需变量的值。
这是您的test.bat
修改内容:
@echo off
setlocal EnableDelayedExpansion
IF NOT DEFINED BAR (
set FOO=1
echo FOO: !FOO!
)
endlocal & set FOO=%FOO%
但是这里还有另一种解决该问题的方法:使用同一个文件中的过程,而不是内联块或外部文件。
@echo off
if not defined BAR call :NotDefined
pause
goto :EOF
:NotDefined
set FOO=1
echo FOO: %FOO%
goto :EOF
答案3
如果这样不起作用,则可能是您延迟了环境变量扩展。您可以关闭它,cmd /V:OFF
或者在 if 中使用感叹号:
@echo off
IF NOT DEFINED BAR (
set FOO=1
echo FOO: !FOO!
)
pause
echo on
答案4
值得注意的是,如果它是同一行上的单个命令,它确实有效。
例如
if not defined BAR set FOO=1
实际上会设置FOO
为 1。然后您可以进行另一项检查,例如:
if not defined BAR echo FOO: %FOO%
嘿,我从来没说过它很漂亮。但如果你不想弄乱 setlocal 或延迟扩展业务,这是一个快速的解决方法。