在 IF 中批处理文件中的变量未被设置?

在 IF 中批处理文件中的变量未被设置?

我有两个非常简单的批处理文件示例:

给变量赋值:

@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 是否也起作用)。

关于您的后续问题,不,没有 就不可能EnableDelayedExpansionsetlocal但是,最初与您相反的行为可用于解决第二个问题:诀窍是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 或延迟扩展业务,这是一个快速的解决方法。

相关内容