批处理脚本中的延迟扩展如何工作?

批处理脚本中的延迟扩展如何工作?

以批处理环境中执行的这一系列简单命令为例:

>set ar[0]=orange
>set ar[1]=apple
>set ar[2]=banana
>for %i in (0,1,2) do echo !ar[%i]!

它输出以下内容:

>echo !ar[0]!
!ar[0]!
>!ar[1]!
!ar[1]!
>!ar[2]!
!ar[2]!

因此它显然不会扩展到其值。我该怎么做?

答案1

资料来源:
第 5 阶段Windows 命令解释器(CMD.EXE)如何解析脚本?

延迟扩展:仅当延迟扩展处于打开状态时,命令才不处于管道两侧的括号块,并且该命令不是“裸”批处理脚本(脚本名称不带括号、CALL、命令连接或管道)。

  • 每个命令的标记都会被独立解析以进行延迟扩展。
    • 大多数命令解析两个或多个标记 - 命令标记、参数标记和每个重定向目标标记。
    • FOR 命令仅解析 IN 子句标记。
    • IF 命令仅解析比较值 - 一个或两个,取决于比较运算符。
  • 对于每个解析的标记,首先检查它是否包含任何!。如果没有,则标记未解析 - 这对于^字符很重要。如果标记确实包含!,则从左到右扫描每个字符:
    • 如果是插入符号 ( ^),则下一个字符没有特殊含义,插入符号本身将被删除
    • 如果是感叹号,则搜索下一个感叹号(不再观察到插入符号),扩展为变量的值。
      • 连续的开口!被折叠成一个!
      • 任何剩余的未配对!将被删除
    • 在这个阶段扩展 vars 是“安全的”,因为不再检测到特殊字符(甚至<CR><LF>
    • 如需更完整的解释,请阅读 dbenham 的后半部分 同一线程-感叹号阶段

5.3)管道加工阶段:仅当命令位于管道的两侧时,
管道的每一侧才会独立且异步地处理。

  • 如果命令是 cmd.exe 的内部命令,或者它是一个批处理文件,或者它是一个带括号的命令块,那么它将通过 在新的 cmd.exe 线程中执行%comspec% /S /D /c" commandBlock",因此命令块会获得阶段重新启动,但这次是在命令行模式下。
    • 如果是带括号的命令块,则所有<LF>前后带有 的命令都转换为<space>&。其他的<LF>都被剥离。
  • 这是管道命令处理的结束。
  • https://stackoverflow.com/q/8192318/1012053有关管道解析和处理的更多信息

阶段5.5)执行重定向:现在执行在第 2 阶段发现的任何重定向。



在此处输入图片描述

  • 您还可以使用Delayed Expansion经过cmd.exe有旗子[/v:on | /v],在/文件。

在此处输入图片描述

set ar[0]=orange
set ar[1]=apple
set ar[2]=banana
for %i in (0,1,2) do cmd.exe /v:on /C"echo !ar[%i]!

  • 在你的/文件无需声明setlocal enabledelayedexpansion,您还可以使用cmd.exe /v:on /c "command & command | command || command..."
@echo off 

set "ar[0]=orange" 
set "ar[1]=apple"
set "ar[2]=banana" 
for %%i in (0,1,2)do cmd /v /c "echo\ !ar[%%i]!"

  • 在你的/声明setlocal enabledelayedexpansion
@echo off 

set "ar[0]=orange"
set "ar[1]=apple"
set "ar[2]=banana" 

setlocal enabledelayedexpansion
for %%i in (0,1,2)do echo\ !ar[%%i]!

enndlocal


@echo off 
set "ar[0]=orange" & set "ar[1]=apple" & set "ar[2]=banana" 
for %%i in (0,1,2)do %ComSpec% /v:on /c"echo !ar[%%i]!"
%__APPDIR__%timeout.exe /t -1 & endlocal & goto :EOF
@echo off 
set "ar[0]=orange" & set "ar[1]=apple" & set "ar[2]=banana" 
setlocal enabledelayedexpansion && for %%i in (0,1,2)do echo\ !ar[%%i]!
%__APPDIR__%timeout.exe /t -1 & endlocal & goto :EOF
@echo off 
set "ar[0]=orange" && set "ar[1]=apple" && set "ar[2]=banana" 
for %%i in (0,1,2)do <con: %ComSpec% /v:on /c"echo !ar[%%i]!"
call <con: rem./ && %__APPDIR__%timeout.exe /t -1 && endlocal
  • 您还可以使用call/或者在命令行中更新该值:
set "ar[0]=orange"
set "ar[1]=apple"
set "ar[2]=banana" 
for %i in (0,1,2)do for %i in (0,1,2)do <con: call echo %ar[%i]%


  • 在你的/文件或命令行,以下命令均相同,注意替换%i%%i在使用的情况下/文件
for %i in (0,1,2) do %ComSpec% /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec% /v:on /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe /v:on /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd /v:on /c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec% /v /r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec% /v /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe /v /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe /v /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd /v /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd /v /c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do %ComSpec%/v/r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd/v/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd/v/c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v:on/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v:on/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd/v:on/c"echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v/r"echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd/v/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd/v/c"echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v/recho !ar[%i]!
for %i in (0,1,2) do %ComSpec%/v/cecho !ar[%i]!

for %i in (0,1,2) do cmd.exe/v/r"echo !ar[%i]!
for %i in (0,1,2) do cmd.exe/v/c"echo !ar[%i]!

for %i in (0,1,2) do cmd/v/recho !ar[%i]!
for %i in (0,1,2) do cmd/v/cecho !ar[%i]!

答案2

延迟扩展主要用于将变量值扩展一次以上,尤其是在For循环中。

引自https://ss64.com/nt/delayedexpansion.html

延迟扩展将导致批处理文件中的变量在执行时而不是在解析时扩展,此选项通过 SETLOCAL EnableDelayedExpansion 命令打开。

变量扩展意味着用其值 C:\WINDOWS 替换变量(例如 %windir%)

默认情况下,扩展只会发生一次,在执行每行之前。 !delayed! 扩展在每次执行行时执行,或者在 FOR 循环命令中的每个循环中执行。首先我们看这个例子:

set i=0
for /l %%a in (0,1,10) do (
  set /a i=%i%+1
  echo %i%
)

这里我们期望输出 1 到 10 的数字。但是,每次它都只输出 0。因为每次变量都会扩展一次,所以值不会改变。

这次我们将尝试延迟扩展:

setlocal enabledelayedexpansion 
set i=0
for /l %%a in (0,1,10) do (
  set /a i=!i!+1
  echo !i!
)

这次我们将得到预期的输出,因为每次我们扩展值时值都会发生变化。

您的代码不起作用,因为您必须setlocal enabledelayedexpansion在代码开始处添加。

希望有帮助

相关内容