pda8888 发表于 2024-3-21 23:17:35

EnableDelayedExpansion 详解(机器翻译+简单校对)

本帖最后由 pda8888 于 2024-3-22 15:32 编辑

原文地址:https://ss64.com/nt/delayedexpansion.html ,由google机器翻译并简单校对,水平有限,可能有错,海涵!


EnableDelayedExpansion
延迟扩展将导致批处理文件中的变量在执行时而不是在解析时扩展,此选项通过SETLOCAL EnableDelayedExpansion命令打开。
变量扩展意味着用C:\WINDOWS 的值替换变量(例如%windir%)

[*]默认情况下,扩展只会发生一次,因为每行都会被解析和执行。
在这种情况下,“行”包含括号内的表达式,即使它们被格式化为跨多行运行。
[*]解析时扩展也适用于%errorlevel%变量,因此在括号表达式中,对%errorlevel%的任何更改仅在括号关闭后才可见。
[*]什么时候!延迟!打开扩展后,每次执行该行或 FOR 循环命令中的每个循环时,变量都会扩展。
使用 FOR、 复合或括号表达式等循环命令,延迟扩展将允许您始终读取变量的当前值。
您可以在有或没有延迟扩展的情况下写入新的变量值。
如果没有延迟扩展,您可以在一行中交换两个变量的值:

Set "var1=%var2%" & set "var2=%var1%"
当延迟扩展生效时,可以使用!variable_name!立即读取变量。
您可以在一行中将两个变量设置为同一内容:

Set "var3=April" & set "var4=!var3!"
您仍然可以读取和使用%variable_name%,并且它将继续显示初始值(在行的开头展开)。
启用延迟扩展的另一种方法是避免使用括号对命令进行分组,而是调用或转到单独的子例程,请参见下面的示例。
例子将其保存为批处理文件,例如 demo.cmd,然后执行(演示)以查看结果:
@echo off
SETLOCAL
Set "_var=first"
Set "_var=second" & Echo %_var%
输出:first
%_var%的值在被 Set 命令更改之前被读入内存。
现在用延迟扩展重复此操作:
@echo off
SETLOCAL EnableDelayedExpansion
Set "_var=first"
Set "_var=second" & Echo %_var% !_var!
这将输出:first second
!_var!的值变量被尽可能晚地替换,而%_var% 变量的工作方式与以前一样。
FOR 循环使用 FOR 循环时,延迟变量扩展通常很有用,通常整个 FOR 循环都会被视为单个命令,即使它跨越批处理脚本的多行。
这是 FOR 循环的默认行为:

@echo off
setlocal
:: 计数到 5 将结果存储在变量
set _tst=0
FOR /l %%G in (1,1,5) Do (echo [%_tst%] & set /a _tst+=1)
echo Total = %_tst%
C:> demo_batch.cmd





Total = 5
请注意,当 FOR 循环完成时,我们得到了正确的总计,因此变量正确地递增,但在循环的每次迭代期间,即使我们设置新值,变量也会顽固地显示其初始值 0。
与EnableDelayedExpansion相同的脚本给出相同的最终结果,但也显示中间值:
@echo off
setlocal EnableDelayedExpansion
:: 计数到 5 将结果存储在变量
set _tst=0
FOR /l %%G in (1,1,5) Do (echo [!_tst!] & set /a _tst+=1)
echo Total = %_tst%
C:\>demo_batch.cmd





Total = 5
与EnableDelayedExpansion相同的脚本给出相同的最终结果,但也显示中间值:
请注意,在 for 循环中我们使用!variable! 而不是%variable%。
另一种编写方法是调用子例程,因为这会跳出循环,因此不需要延迟扩展
@echo off
setlocal
:: 计数到 5 将结果存储在变量
set _tst=0
FOR /l %%G in (1,1,5) Do (call :sub %%G)
echo Total = %_tst%
goto :eof

:sub
echo [%1] & set /a _tst+=1
goto :eof


C:\> demo_batch.cmd





Total = 5
使用 %var% 和 !var!通过延迟扩展,单个变量可以保存两个值:
@echo off
Setlocal EnableDelayedExpansion
Set "_var=Old"
For /L %%G in (1,1,3) Do (
 Set "_var=New"
 Echo [%_var%] is now [!_var!]
)
输出:
is now
is now
is now

请注意,变量在循环的每次迭代中恢复为旧值,不需要 Set _var=%_var%
这是因为 FOR 循环的每次迭代将启动一个新的批处理文件上下文以及任何指定的参数。
其他效果 - 标点符号因为 DelayedExpansion 稍后会扩展变量,这意味着表达式中的任何转义字符(^) 和重定向字符都将在变量扩展之前进行计算,这非常有用:
@echo off
Setlocal
Set _html=Hello^>World
Echo %_html%
在上面,Echo 命令将创建一个名为“world”的文本文件 - 不完全是我们想要的!这是因为变量在解析时扩展,因此最后一行正在执行Echo Hello > World并且 > 字符被解释为重定向运算符。
如果我们现在尝试使用EnableDelayedExpansion进行同样的操作:
Setlocal EnableDelayedExpansion
Set _html=Hello^>World
Echo !_html!
通过延迟扩展,变量(包括>)仅在执行时扩展,因此>字符永远不会被解释为重定向运算符。
这使得在变量中使用 HTML 和 XML 格式的字符串成为可能。
当启用延迟扩展并且一行中至少存在一个感叹号时,任何插入符号都将被解释为转义符,因此将从输出中消失:
Setlocal EnableDelayedExpansion
Echo "Hello^World"
Echo "Hello^World!"
上面输出:
"Hello^World"
"HelloWorld"

即使您将插入符号 ^^ 加倍(通常用作转义符),或者在感叹号之前添加转义符,行中任何位置存在感叹号仍然会产生这种效果。
更多示例在 FOR 命令中设置并回显相同的变量:
Setlocal EnableDelayedExpansion
for /f %%G in ("abc") do ( set _demo=%%G & echo !_demo!)
使用另一个变量的值替换variable_name:
@echo off
setlocal EnableDelayedExpansion
Set var1=Hello ABC how are you
Set var2=ABC
Set result=!var1:%var2%=Beautiful!
Echo [!result!]
另一种用另一个变量的内容替换命名变量的方法是CALL SET
使用延迟变量扩展时的一些意外行为如果DelayedExpansion与循环一组文件的 FOR 命令结合使用,并且该组中的任何文件有感叹号“!” 在filename中,它将被解释为!variable! 。
尽管这不是文件名中常用的字符,但它可能会导致脚本失败。发生这种情况是因为参数扩展 (%%P) 发生在延迟扩展阶段尝试解释my!filen!ame.txt之前
当DelayedExpansion在输出为Piped的代码块(括号之间分组的一个或多个命令)内使用时,将跳过变量扩展。 当您使用管道时,管道的两个部分都将在新的 cmd.exe 实例中执行,并且默认情况下这些实例会在禁用延迟扩展的情况下启动。
为什么会有这种行为?SET 命令于 1983 年 3 月首次随 MS-DOS 2.0 引入,当时内存和 CPU 非常有限,每行一次变量扩展就足够了。
大约 16 年后的 1999 年,延迟扩展被引入,当时已经使用早期语法编写了数百万个批处理文件。保留立即扩展作为默认值保留了与现有批处理文件的向后兼容性。
如果从头开始,这不是任何人都会设计语言的方式,PowerShell 的行为确实是这样的:
PS C:\> $demo = "First"
PS C:\> $demo = "Second" ; echo $demo
Second
默认行为默认情况下禁用 EnableDelayedExpansion。还可以通过使用/v
开关 启动CMD来启用 EnableDelayedExpansion 。
开启延迟扩展后,可以使用SETLOCAL DisableDelayedExpansion再次关闭延迟扩展
EnableDelayedExpansion 可以在 HKLM 或 HKCU 下的注册表中设置为默认值:

"DelayedExpansion"= (REG_DWORD)
1=enabled 0=disabled (default)

不建议更改此默认值。
除非您运行的每个批处理脚本都以适当的SETLOCAL DisableDelayedExpansion或SETLOCAL EnableDelayedExpansion命令开始,否则更改默认值可能会破坏某些脚本。
相关命令论坛讨论- EnableDelayedExpansion(非常感谢 Jeb 和 Aacini 澄清了很多观点)。
OldNewThing - EnableDelayedExpansion 的更长解释。
SETLOCAL - 在批处理文件中开始本地化环境更改。



yc2428 发表于 2024-3-21 23:40:41

谢谢分享

2012飘水 发表于 2024-3-22 00:51:08

好好学习,天天向上,感谢楼主

hehuiying 发表于 2024-3-22 02:37:33

谢谢分享

wang1126 发表于 2024-3-22 06:07:11

谢谢分享

yyz2191958 发表于 2024-3-22 07:56:09

谢谢分享

oh312 发表于 2024-3-22 08:56:27

赞,谢谢分享。

axiang117 发表于 2024-3-22 11:07:16

看了半天也没看明白,可能是我太笨了{:1_185:}

guong 发表于 2024-3-22 12:37:00

谢谢分享

pda8888 发表于 2024-3-22 15:31:06

axiang117 发表于 2024-3-22 11:07
看了半天也没看明白,可能是我太笨了

总之一句话:在代码块中,即:for循环或if这样的有括号情形,括号内视作为一条指令,括号里面的变量发生变化,如果不启用变量延迟并用对应的表达式,是不会有正确结果的。
页: [1]
查看完整版本: EnableDelayedExpansion 详解(机器翻译+简单校对)