SETLOCAL和ENABLEDELAYEDEXPANSION如何工作?

时间:2011-07-13 13:44:19

标签: cmd batch-file

我注意到在大多数脚本中,两者通常都在同一行:

SETLOCAL ENABLEDELAYEDEXPANSION

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

如果脚本设置在脚本的第一行并且在脚本结束之前未禁用,那么设置ENABLEDELAYEDEXPANSION是否会对脚本产生负面影响?

4 个答案:

答案 0 :(得分:91)

我认为您应该了解延迟扩展的原因。现有的答案没有解释(充分)恕我直言。

键入SET /?可以很好地解释这个问题:

  

延迟环境变量扩展对于四处走动非常有用   一线的当前扩张的局限性   读取文本,而不是执行时。以下示例   演示了立即变量扩展的问题:

set VAR=before
if "%VAR%" == "before" (
    set VAR=after
    if "%VAR%" == "after" @echo If you see this, it worked
)
     

永远不会显示消息,因为BOTH IF语句中的%VAR%   在读取第一个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%

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

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

这会打印x2y2:每1个被2替换。

如果没有setlocal enabledelayedexpansion,感叹号就是这样,所以它会回显!b:1=2!两次。

因为当(块)语句读取时会扩展正常环境变量,所以展开%b:1=2%会使用循环前的值bz2(但未设置y2

答案 1 :(得分:19)

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

它的效果在脚本的持续时间内生效,或者ENDLOCAL

  

当到达批处理脚本的末尾时,隐含的ENDLOCAL是   为该批次发出的任何未完成的SETLOCAL命令执行   脚本。

特别是,这意味着如果您在脚本中使用SETLOCAL ENABLEDELAYEDEXPANSIONany environment variable changes are lost at the end of it除非take special measures

答案 2 :(得分:8)

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

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

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

答案 3 :(得分:0)

通常存在一个真正的问题,因为当批处理文件完成时,不会导出内部设置的任何变量。所以它不可能出口,这引起了我们的问题。因此,我只是将注册表设置为ALWAYS使用延迟扩展(我不知道为什么它不是默认值,可能是速度或遗留兼容性问题。)