【问题标题】:Escaping exclamation marks required in replace string but not in search string (substring replacement with delayed expansion on)?替换字符串中需要转义感叹号,但搜索字符串中不需要(延迟扩展的子字符串替换)?
【发布时间】:2016-07-15 11:52:50
【问题描述】:

假设在启用delayed expansion 时,想要使用substring replacement 语法用感叹号替换某些子字符串,他们必须使用立即(正常)扩展,因为解析器无法区分!s 用于扩展和文字那些。

但是,为什么必须在替换字符串中转义感叹号?以及为什么搜索字符串中的感叹号转义没有必要甚至破坏性?

以下脚本将字符串中的!s 替换为`,然后以相反的顺序替换,所以我希望结果等于初始字符串(当然,它本身不能包含任何反引号):

@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem This is the test string:
set "STRING=string!with!exclamation!marks!"
set "DELOFF=%STRING%"
set "DELOFF=%DELOFF:!=`%"
set "DELOFF=%DELOFF:`=!%"
setlocal EnableDelayedExpansion
set "DELEXP=!STRING!"
set "DELEXP=%DELEXP:!=`%"
set "DELEXP=%DELEXP:`=!%"
echo(original   string: !STRING!
echo(normal  expansion: !DELOFF!
echo(delayed expansion: !DELEXP!
endlocal
endlocal
exit /B

这个结果肯定不是我想要的,最后一个字符串不一样:

original   string: string!with!exclamation!marks!
normal  expansion: string!with!exclamation!marks!
delayed expansion: stringexclamation

只要走线...:

set "DELEXP=%DELEXP:`=!%"

....并在那里将! 替换为^!,因此在替换字符串中转义了感叹号,结果正是我所期望的:

original   string: string!with!exclamation!marks!
normal  expansion: string!with!exclamation!marks!
delayed expansion: string!with!exclamation!marks!

当我尝试其他转义组合时(在替换和搜索字符串中转义感叹号,或仅在后者中转义),结果再次是上述不需要的。

我浏览了How does the Windows Command Interpreter (CMD.EXE) parse scripts? 的帖子,但我找不到对这种行为的解释,因为我知道正常(或立即,百分比)扩展是在延迟扩展发生之前很久就完成的,甚至可以识别任何感叹号。插入符号的识别和转义似乎也在之后发生。此外,字符串周围甚至还有引号,通常会在解析器中隐藏插入符号。

【问题讨论】:

    标签: batch-file replace cmd escaping delayedvariableexpansion


    【解决方案1】:

    实际上,对于子字符串替换本身,不需要转义。只有后面的解析阶段才有必要。这就是为什么:

    但是,为什么要在替换字符串中转义感叹号?

    问题是,立即(正常,%)扩展是在相当早的阶段完成的,而延迟扩展(!),顾名思义,是作为最后步骤之一完成的。因此,立即展开的弦也会经过延迟展开阶段。作为证明,将变量VAR设置为Value!X!,将X设置为0,然后执行echo %VAR%,结果将得到Value0
    但是回到最初的问题,当使用立即子串替换时,替换字符串是扩展值的一部分,所以也经过了延迟扩展阶段。因此,必须将文字感叹号转义,以免被延迟扩展所消耗。这意味着替换本身不需要转义,它实际上是在之后完成的,因此包含转义的给定替换字符串是按字面意思应用的。

    为什么搜索字符串中的感叹号被转义没有必要甚至破坏性?

    由于插入符号识别和转义发生在立即展开之后,因此搜索字符串按字面意思处理。此外,搜索字符串被替换,因此不包括在立即子字符串替换的输出中,因此它不会通过延迟扩展阶段。


    让我们看一下原始示例(仅摘录):

    set "STRING=string!with!exclamation!marks!"
    setlocal EnableDelayedExpansion
    set "DELEXP=!STRING!"
    set "DELEXP=%DELEXP:!=`%"
    set "DELEXP=%DELEXP:`=!%"
    echo(delayed expansion: !DELEXP!
    endlocal
    

    替换 set "DELEXP=%DELEXP:!=`%" 搜索 !。结果值为string`with`exclamation`marks`

    使用set "DELEXP=%DELEXP:^!=`%" 会按字面意思搜索^!,所以当然不会找到任何出现的地方(所以保留原字符串中的所有文字!,最后通过延迟扩展处理)。

    替换set "DELEXP=%DELEXP:`=!%" 完美地替换`!,结果字符串为string!with!exclamation!marks!,但是这会被之后的延迟扩展所消耗。

    转义替换 %DELEXP:`=^!%` 替换为 ^! 字面意思,所以结果是 string^!with^!exclamation^!marks^!;之后在延迟扩展阶段处理转义,最终得到文字 ! 和返回字符串 string!with!exclamation!marks!


    根据How does the Windows Command Interpreter (CMD.EXE) parse scripts?的帖子,还有第二个阶段发生转义,即延迟扩展阶段。这适用于原始问题中的示例,因为第一个转义(在特殊字符识别阶段)由于周围的引号而被禁用(省略这些会导致需要像^^! 这样的双重转义)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-04
      • 1970-01-01
      • 1970-01-01
      • 2012-04-03
      相关资源
      最近更新 更多