【问题标题】:Example of delayed expansion in batch file批处理文件中延迟扩展的示例
【发布时间】:2012-05-20 11:03:39
【问题描述】:

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

【问题讨论】:

    标签: windows batch-file cmd environment-variables delayedvariableexpansion


    【解决方案1】:

    看看下面的例子……

    示例1:下面的代码没有使用延迟扩展,所以for循环中的变量只扩展了一次。这意味着 %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 并使用! 而不是% 扩展变量时,每次都会重新扩展该变量,并且一切正常。

    【讨论】:

    • 很好的例子。但更好的是使用set /A COUNT=COUNT + 1 或更短的set /A COUNT+=1 来证明在算术表达式 中可以仅使用它们的名称来引用变量。 算术表达式set /A之后的字符串。在命令提示符窗口中运行的帮助输出 set /? 解释了 IFFOR 示例的延迟扩展以及算术表达式中的特殊变量解析。
    • 值得澄清的是,在解析一行时会发生扩展,并且对于由括号分隔的块(例如if块),整个块都算作一个“行”(source )。
    【解决方案2】:

    我想添加一个很好的示例,说明“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 将首先翻译 % 括起来的变量,然后处理 ! 括起来的变量并且(在这种情况下)将结果推送到“定位”变量中。

    【讨论】:

      【解决方案3】:

      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
      

      请注意,上面的脚本显示proper ways of escaping

      • % 百分号由%% 加倍(延迟扩展无关紧要),并且
      • ! 感叹号如果启用延迟扩展:
        • "^!" 如果用双引号括起来,则使用 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!
      
      ==>
      

      【讨论】:

        【解决方案4】:

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

        虽然它在其他情况下也很有用。

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

        @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"
        

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

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

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

        【讨论】:

          猜你喜欢
          • 2013-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-25
          相关资源
          最近更新 更多