SETLOCAL 和 ENABLEDELAYEDEXPANSION 如何工作?

问题描述 投票:0回答:5

我注意到在大多数脚本中,两者通常位于同一行,如下所示:

SETLOCAL ENABLEDELAYEDEXPANSION

这两个实际上是独立的命令并且可以写在不同的行上吗?

如果将

ENABLEDELAYEDEXPANSION
设置在脚本的第一行并且直到脚本末尾才禁用,是否会对脚本产生不利影响?

batch-file cmd variable-expansion delayedvariableexpansion
5个回答
141
投票

我想你应该明白什么是延迟扩张。现有的答案没有(充分)解释它。

输入

SET /?

可以很好地解释这一点:

延迟环境变量扩展对于解决问题很有用 当前扩展的局限性,当一行 文本是在读取时读取的,而不是在执行时读取的。 下面的例子 演示了立即变量扩展的问题:

set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
  
  
永远不会显示该消息,因为两个 IF 语句中都有 %VAR% 当读取第一个 IF 语句时被替换,因为它在逻辑上 包括 IF 的主体,它是一个复合语句。 所以如果 复合语句内部实际上是在比较“before”与 “之后”永远不会相等。 同样,下面的例子 不会按预期工作:

set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
  
  
因为它不会在当前目录中建立文件列表, 而是将 LIST 变量设置为最后找到的文件。 同样,这是因为当 FOR 时 %LIST% 仅扩展一次 语句被读取,此时 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%


另一个例子是这个批处理文件:

@echo off setlocal enabledelayedexpansion set b=z1 for %%a in (x1 y1) do ( set b=%%a echo !b:1=2! )

这会打印

x2

y2
:每个 1 都会被 2 替换。

如果没有

setlocal enabledelayedexpansion

,感叹号就是这样,所以它会回显两次
!b:1=2!

因为当(块)语句是

read 时,普通环境变量会展开,因此展开 %b:1=2%

 使用循环之前 
b
 的值:
z2
(但未设置时为 
y2
)。


32
投票

ENABLEDELAYEDEXPANSION

是传递给
SETLOCAL
命令的参数(查看
setlocal /?

其效果在脚本持续时间内有效,或

ENDLOCAL

当到达批处理脚本末尾时,隐含的

ENDLOCAL

 是
  对该批次发出的任何未完成的 
SETLOCAL
 命令执行
  脚本。

特别是,这意味着如果您在脚本中使用

SETLOCAL ENABLEDELAYEDEXPANSION

,则
任何环境变量更改都会在脚本结束时丢失,除非您采取特殊措施


12
投票
ENABLEDELAYEDEXPANSION 部分在某些使用延迟扩展的程序中是必需的,即通过将变量名称括在感叹号中来获取在 IF 或 FOR 命令内修改的变量的值。

如果您在不需要此扩展的脚本中启用此扩展,则仅当脚本包含用感叹号 !LIKE! 括起来的名称时,该脚本的行为才会有所不同。 !这些!。通常名称只是被删除,但如果偶然存在同名变量,那么结果是不可预测的,并且取决于该变量的值和它出现的位置。

SETLOCAL 部分在少数专门(递归)程序中是必需的,但当您希望确保不会偶然修改任何具有相同名称的现有变量,或者如果您想自动删除在中使用的所有变量时,通常使用 SETLOCAL 部分。你的程序。但是,由于没有单独的命令来启用延迟扩展,因此需要此功能的程序还必须包含 SETLOCAL 部分。


1
投票
经常存在一个真正的问题,因为当该批处理文件完成时,内部设置的任何变量都不会被导出。所以它不可能出口,这给我们带来了问题。结果,我只是将注册表设置为始终使用延迟扩展(我不知道为什么它不是默认值,可能是速度或遗留兼容性问题。)


0
投票
无法将我的解决方案作为评论发布,所以在这里。如果可以的话,请相应排序。

@jeb 要使用 SETLOCAL ENABLEDELAYEDEXPANSION 显示感叹号,您需要对它们进行双重转义:

FOR %%a in (TEST.CMD) do if exist %%a echo File exist^^! & echo "Caret^" is not gone and back again^^!


    

© www.soinside.com 2019 - 2024. All rights reserved.