51

有人可以给我一个例子,说明批处理脚本在有或没有延迟扩展的情况下会有所不同吗?是否存在您不想使用延迟扩展的情况?谢谢。

4

4 回答 4

68

看看下面的例子......

示例1:下面的代码没有使用延迟扩展,所以for循环中的变量只被扩展了一次。这意味着无论我们使用 set 命令对它做什么,它%Count%都会在循环的每次迭代中展开:0

@echo off
set COUNT=0

for %%v in (1 2 3 4) do (
  set /A COUNT=%COUNT% + 1
  echo Count = %COUNT%
)
pause

所以这个脚本会输出:

Count = 0
Count = 0
Count = 0
Count = 0

这不是这个循环应该如何工作的。

示例 2:另一方面,如果我们使用延迟扩展,我们有以下脚本,它将按预期运行。

setlocal ENABLEDELAYEDEXPANSION
set COUNT=0

for %%v in (1 2 3 4) do (
  set /A COUNT=!COUNT! + 1
  echo Count = !COUNT!
)

pause

并且,正如预期的那样,它将输出:

Count = 1
Count = 2
Count = 3
Count = 4

当您使用ENABLEDELAYEDEXPANSION, 并使用!而不是扩展变量%时,每次都会重新扩展该变量,并且一切都按预期进行。

于 2012-05-11T21:30:31.510 回答
10

我想添加一个很好的示例,说明“EnableDelayedExpansion”(EDE)如何在无处不在的 FOR 循环示例之外发挥作用。

这是我希望解析的一行地震数据(我称之为 1line.txt)

ak_11574812 2015.04.29.193822 62.9525 -148.8849 1.0 9.5 1 49km S of Cantwell, Alaska

我遇到的问题是这一行的最后一段并不总是从相同的列号开始。所以我需要创建一个灵活的 SET 命令,它可以准确地提取出这一行的最后一段。

ECHO OFF
setlocal enableDelayedExpansion
set where=72
set /p line=<1line.txt
set locate=!line:~%where%,28!
echo %locate%

EDE 允许我将一个变量 (where) 放在另一个变量 (line) 中。EDE 将首先翻译 % 括起来的变量,然后处理 ! 括起来的变量 并且(在这种情况下)将结果推送到“定位”变量中。

于 2015-04-30T19:26:07.637 回答
8

Max 的回答给出了一个示例,说明在有或没有延迟扩展的情况下,批处理脚本的行为会有所不同

为了完整起见,让我们回答问题的另一部分并展示当您的数据包含感叹号时您不想使用延迟扩展的!情况(并展示处理此类数据的两种方式):

@ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion

  set "_auxFile=%temp%\%~n0.txt"
  rem create multiline sample file
  >"%_auxFile%" ( for /L %%G in (1,1,3) do echo line %%G is 100%% valid! Sure! Hurrah!)
  rem create one-line sample file
  >"%_auxFile%" echo this line is 100%% valid! Sure! Hurrah!

  echo(
  echo --- file content 
  type "%_auxFile%"

  echo(
  SETLOCAL EnableDelayedExpansion
    echo --- enabled delayed expansion chokes down unescaped exclamation marks^^^! "^!"
    for /F "usebackq delims=" %%G in ("%_auxFile%") do (
      set "_auxLine=%%~G"
      echo loop var=%%~G
      echo _auxLine=!_auxLine!
    )
  ENDLOCAL
  echo(
  SETLOCAL DisableDelayedExpansion
    echo --- toggled delayed expansion works although might be laborious!
    for /F "usebackq delims=" %%G in ("%_auxFile%") do (
      set "_auxLine=%%G"
      echo loop var=%%G
      SETLOCAL EnableDelayedExpansion
        echo _auxLine=!_auxLine!
      ENDLOCAL
    )
  ENDLOCAL
  echo(
  SETLOCAL DisableDelayedExpansion
    echo --- keep delayed expansion DISABLED: use CALL command!
    for /F "usebackq delims=" %%G in ("%_auxFile%") do (
      set "_auxLine=%%G"
      echo loop var=%%G
      call :ProcessVar
    )
  ENDLOCAL

  rem delete the sample file
  del "%_auxFile%"
ENDLOCAL
goto :eof

:ProcessVar
  echo _auxLine=%_auxLine%
  echo WARNING: neither !_auxLine! nor %%G loop variable is available here!  
goto :eof

请注意,上面的脚本显示了正确的转义方式

  • %百分号%%加倍(延迟扩展无关紧要),和
  • !如果启用延迟扩展,感叹号:
    • "^!"如果用一对双引号括起来,则使用cmd和批处理脚本通用转义字符^插入符号;
    • ^^^!否则,使用三个^插入符号。

输出

==> D:\bat\SO\10558316.bat

--- file content
this line is 100% valid! Sure! Hurrah!

--- enabled delayed expansion chokes down unescaped exclamation marks! "!"
loop var=this line is 100% valid Hurrah
_auxLine=this line is 100% valid Hurrah

--- toggled delayed expansion works although might be laborious!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!

--- keep delayed expansion DISABLED: use CALL command!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!
WARNING: !_auxLine! as well as %G loop variables are not available here!

==>
于 2016-06-25T10:26:43.880 回答
0

正如答案中所指出的,延迟扩展的主要用途是在括号上下文中设置和访问变量。

尽管它在其他情况下也很有用。

参数化子字符串和字符串替换:

@echo off
setlocal enableDelayedExpansion

set "string=test string value"
set start=5
set get_next=6

echo #!string:~%start%,%get_next%!#

set "search_for=string"
set "replace_with=text"

echo #!string:%search_for%=%replace_with%!#

输出将是:

#string#
#test text value#

虽然这可以通过额外的调用来实现,但这种方式更高效

在括号内使用 shift 命令参数化参数访问

@echo off

echo first attempt:
(
    echo "%~1"
    shift
    echo "%~1"
)
::now the shift command will take effect

setlocal enableDelayedExpansion
echo second attempt:

(   
    set /a argument=1
    call echo %%!argument! 
    shift
    call echo %%!argument! 
)

输出将是:

first attempt:
"first argument"
"first argument"
second attempt:
"second argument"
"third argument"

如您所见,参数化参数访问只能通过延迟扩展来完成。

使用 for 标记(或函数参数)进行参数化

另一种混合!s 和%s 的方法可能对嵌套循环有用:

@echo off
setlocal enabledelayedexpansion
set begin=2
set end=2
set string=12345

for /f "tokens=1,2" %%A in ("!begin! !end!") do set "string2=!string:~%%A,%%B!"
echo !string2!

endlocal

正如您现在所看到的,for 命令标记用作参数。

于 2021-01-26T18:39:18.793 回答