无忧启动论坛

标题: EnableDelayedExpansion 详解(机器翻译+简单校对) [打印本页]

作者: pda8888    时间: 2024-3-21 23:17
标题: EnableDelayedExpansion 详解(机器翻译+简单校对)
本帖最后由 pda8888 于 2024-3-22 15:32 编辑

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


EnableDelayedExpansion
延迟扩展将导致批处理文件中的变量在执行时而不是在解析时扩展,此选项通过SETLOCAL EnableDelayedExpansion命令打开。
变量扩展意味着用C:\WINDOWS 的值替换变量(例如%windir%)
使用 FOR、 复合或括号表达式等循环命令,延迟扩展将允许您始终读取变量的当前值。
您可以在有或没有延迟扩展的情况下写入新的变量值。
如果没有延迟扩展,您可以在一行中交换两个变量的值:

  1. Set "var1=%var2%" & set "var2=%var1%"
复制代码

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

  1. Set "var3=April" & set "var4=!var3!"
复制代码

您仍然可以读取和使用%variable_name%,并且它将继续显示初始值(在行的开头展开)。
启用延迟扩展的另一种方法是避免使用括号对命令进行分组,而是调用转到单独的子例程,请参见下面的示例。
例子将其保存为批处理文件,例如 demo.cmd,然后执行(演示)以查看结果:
  1. @echo off
  2. SETLOCAL
  3. Set "_var=first"
  4. Set "_var=second" & Echo %_var%
复制代码
输出:first

%_var%的值在被 Set 命令更改之前被读入内存。
现在用延迟扩展重复此操作:
  1. @echo off
  2. SETLOCAL EnableDelayedExpansion
  3. Set "_var=first"
  4. Set "_var=second" & Echo %_var% !_var!
复制代码
这将输出:first second

!_var!的值变量被尽可能晚地替换,而%_var% 变量的工作方式与以前一样。
FOR 循环
使用 FOR 循环时,延迟变量扩展通常很有用,通常整个 FOR 循环都会被视为单个命令,即使它跨越批处理脚本的多行。
这是 FOR 循环的默认行为:

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

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

C:\>demo_batch.cmd
[0]
[1]
[2]
[3]
[4]
Total = 5

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

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

复制代码
C:\> demo_batch.cmd
[1]
[2]
[3]
[4]
[5]
Total = 5

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

请注意,变量在循环的每次迭代中恢复为旧值,不需要 Set _var=%_var%
这是因为 FOR 循环的每次迭代将启动一个新的批处理文件上下文以及任何指定的参数。
其他效果 - 标点符号
因为 DelayedExpansion 稍后会扩展变量,这意味着表达式中的任何转义字符(^) 和重定向字符都将在变量扩展之前进行计算,这非常有用:
  1. @echo off
  2. Setlocal
  3. Set _html=Hello^>World
  4. Echo %_html%
复制代码

在上面,Echo 命令将创建一个名为“world”的文本文件 - 不完全是我们想要的!这是因为变量在解析时扩展,因此最后一行正在执行Echo Hello > World并且 > 字符被解释为重定向运算符。
如果我们现在尝试使用EnableDelayedExpansion进行同样的操作:
  1. Setlocal EnableDelayedExpansion
  2. Set _html=Hello^>World
  3. Echo !_html!
复制代码

通过延迟扩展,变量(包括>)仅在执行时扩展,因此>字符永远不会被解释为重定向运算符。
这使得在变量中使用 HTML 和 XML 格式的字符串成为可能。
当启用延迟扩展并且一行中至少存在一个感叹号时,任何插入符号都将被解释为转义符,因此将从输出中消失:
  1. Setlocal EnableDelayedExpansion
  2. Echo "Hello^World"
  3. Echo "Hello^World!"
复制代码
上面输出:
"Hello^World"
"HelloWorld"

即使您将插入符号 ^^ 加倍(通常用作转义符),或者在感叹号之前添加转义符,行中任何位置存在感叹号仍然会产生这种效果。
更多示例
在 FOR 命令中设置并回显相同的变量:
  1. Setlocal EnableDelayedExpansion
  2. for /f %%G in ("abc") do ( set _demo=%%G & echo !_demo!)
复制代码

使用另一个变量的值替换variable_name:
  1. @echo off
  2. setlocal EnableDelayedExpansion
  3. Set var1=Hello ABC how are you
  4. Set var2=ABC
  5. Set result=!var1:%var2%=Beautiful!
  6. 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 下的注册表中设置为默认值:
[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"DelayedExpansion"= (REG_DWORD)
1=enabled 0=disabled (default)

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




作者: yc2428    时间: 2024-3-21 23:40
谢谢分享
作者: 2012飘水    时间: 2024-3-22 00:51
好好学习,天天向上,感谢楼主
作者: hehuiying    时间: 2024-3-22 02:37
谢谢分享
作者: wang1126    时间: 2024-3-22 06:07
谢谢分享
作者: yyz2191958    时间: 2024-3-22 07:56
谢谢分享
作者: oh312    时间: 2024-3-22 08:56
赞,谢谢分享。
作者: axiang117    时间: 2024-3-22 11:07
看了半天也没看明白,可能是我太笨了
作者: guong    时间: 2024-3-22 12:37
谢谢分享
作者: pda8888    时间: 2024-3-22 15:31
axiang117 发表于 2024-3-22 11:07
看了半天也没看明白,可能是我太笨了

总之一句话:在代码块中,即:for循环或if这样的有括号情形,括号内视作为一条指令,括号里面的变量发生变化,如果不启用变量延迟并用对应的表达式,是不会有正确结果的。




欢迎光临 无忧启动论坛 (http://wuyou.net/) Powered by Discuz! X3.3