批处理文件中延迟扩展的示例

61

有人能给我一个批处理脚本在启用延迟环境变量和不启用时会表现出不同的示例吗?是否有任何情况下您不想使用延迟环境变量?谢谢。

5个回答

79

看下面的例子...

例子1:以下代码没有使用延迟扩展,因此循环中的变量只被扩展一次。这意味着%Count%在每次循环迭代中都会扩展为0,无论我们使用set命令对其做什么操作:

@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,并使用!而不是%来扩展变量时,每次重新扩展变量,并且一切都按照预期进行。


5
好的例子。但更好的方法是使用set /A COUNT=COUNT + 1或更短的set /A COUNT+=1,以展示在算术表达式中变量可以只用名字引用。算术表达式是在set /A之后的字符串。在命令提示符窗口中运行set /?的帮助输出解释了延迟扩展的 IFFOR 示例,以及算术表达式中的特殊变量解析。 - Mofi
值得澄清的是,扩展发生在解析一行时,对于由括号(例如 if 块)分隔的块,整个块都被视为“一行”(来源)。 - OfirD

11

我想举一个很好的例子,说明“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将首先翻译由%括起来的变量,然后处理由!和(在本例中)推出结果到“locate”变量括起来的变量。


9

Max's answer提供了一个批处理脚本在启用或禁用延迟扩展时的区别示例。

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

@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 和批处理通用转义字符 ^ caret 的 "^!"
    • 否则,使用三个 ^ carets 的 ^^^!

输出

==> 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!

==>

0

这里有几个回答解答了“如何使用延迟扩展?”的问题或者如果不使用延迟扩展会发生什么。然而,第二个问题是“是否存在任何情况下你不想使用延迟扩展?”一些答案将这个问题视为“如何避免使用延迟扩展所引起的问题?”

我的回答解答了我理解的问题:“在哪些情况下最好不使用延迟扩展(而是使用它)?”

如果你想交换两个变量的内容,最简单的方法是使用%standard%变量扩展:

set "var1=%var2%"  &  set "var2=%var1%"

%standard%扩展的工作方式使得可以在不使用任何辅助变量的情况下实现此替换。据我所知,“批处理语言”是唯一允许以这种方式执行此交换的编程语言,也就是说,充分利用语言“特性”(而不是通过专门的“交换”指令/语句)。


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"

正如您所看到的,只有使用延迟扩展才能访问参数化参数。

使用令牌(或函数参数)进行参数化

另一种混合使用!%的方法,这对于嵌套循环可能非常有用:

@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命令标记被用作参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接