假设有人想在启用substring replacement时使用delayed expansion语法用感叹号替换某些子字符串,则必须使用立即(正常)扩展,因为解析器无法区分{{1}用于扩展和文字扩展。
但是,为什么必须在替换字符串中删除感叹号?当搜索字符串中的感叹号被转义时,为什么它没有必要甚至是破坏性的?
以下脚本用!
替换字符串中的!
,然后以相反的顺序替换,所以我希望结果等于初始字符串(不能包含任何反向标记)它当然是):
`
这个结果绝对不是我想要的,最后一个字符串是不同的:
@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:`=!%"
替换为!
,因此转义替换字符串中的感叹号,结果正是我所期望的:
^!
当我尝试其他转义组合时(在替换和搜索字符串中转义感叹号,或仅在后者中转义感叹号),结果再次出现上述不需要的组合。
我走过帖子How does the Windows Command Interpreter (CMD.EXE) parse scripts?,但我无法找到对此行为的解释,因为我了解到正常(或即时,百分比)扩展是在延迟扩展发生之前很久就完成的,并且任何感叹号都被识别。此外,似乎还发生了插入符号识别和转义。此外,字符串周围甚至还有引号,通常会将解析符隐藏在解析器中。
答案 0 :(得分: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?,存在第二阶段,其中发生逃逸,这是延迟扩展阶段。这是适用于原始问题中的示例的那个,因为第一次转义(在特殊字符识别阶段期间)由于周围的引号而被禁用(省略这样会导致需要双重转义,如{{1 }})。