为什么在for/F`或`for/R`的参数中不需要转义感叹号?

asc*_*pfl 7 syntax for-loop cmd batch-file

当启用延迟扩展时,(通常)必须正确地转义感叹号以获得文字符号(例如,^^!或者,当介于两者之间""^!,为了获得文字!).

然而,为什么没有感叹号的逃逸后立即提供的参数范围内,标志着必要的,但即使distructive /R或者/F在开关for循环命令?


考虑到上述转义规则,我创建了以下批处理脚本,for /R并使用名称作为参数创建了一个目录!(在/R选项后面说明):

@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "TARGET=excl^!dir"
set "FILE=empty_%RANDOM%.txt"
echo/
echo creating directory: !TARGET!
mkdir "!TARGET!"
echo placing empty file: !TARGET!\%FILE%
> "!TARGET!\%FILE%" break
echo/
echo adequately escaped `^^!`:
for /R "excl^!dir" %%F in ("*.*") do echo(found item: %%~nxF
for /R excl^^!dir %%F in ("*.*") do echo(found item: %%~nxF
echo/
echo improperly escaped `^^!`:
for /R "excl!dir" %%F in ("*.*") do echo(found item: %%~nxF
for /R excl!dir %%F in ("*.*") do echo(found item: %%~nxF
for /R excl^!dir %%F in ("*.*") do echo(found item: %%~nxF

erase "!TARGET!\%FILE%"
rmdir "!TARGET!"

endlocal
exit /B
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,目录只是枚举而不转义!.这是一个输出:

creating directory: excl!dir
placing empty file: excl!dir\empty_18378.txt

adequately escaped `!`:

improperly escaped `!`:
found item: empty_18378.txt
found item: empty_18378.txt
found item: empty_18378.txt
Run Code Online (Sandbox Code Playgroud)

我期待目录的情况下,感叹号被列举其实正确转义,否则不会; 但结果显示了相反的行为.


出现类似的现象for /F,如下面的脚本中带有!选项字符串的脚本:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "STRING=EXCLAMATION ^! MARK AND CARET ^^ SYMBOL"
echo/
echo normal  expansion: %STRING%
echo delayed expansion: !STRING!
echo/
echo adequately escaped `^^!`:
for /F "tokens=1-2 delims=^!" %%K in ("!STRING!") do echo(%%K -- %%L
for /F tokens^=1-2^ delims^=^^! %%K in ("!STRING!") do echo(%%K -- %%L
echo/
echo improperly escaped `^^!`:
for /F "tokens=1-2 delims=!" %%K in ("!STRING!") do echo(%%K -- %%L
for /F tokens^=1-2^ delims^=! %%K in ("!STRING!") do echo(%%K -- %%L
for /F tokens^=1-2^ delims^=^! %%K in ("!STRING!") do echo(%%K -- %%L

endlocal
exit /B
Run Code Online (Sandbox Code Playgroud)

输出看起来像这样,奇怪的是:

normal  expansion: EXCLAMATION  MARK AND CARET  SYMBOL
delayed expansion: EXCLAMATION ! MARK AND CARET ^ SYMBOL

adequately escaped `!`:
EXCLAMATION  --  MARK AND CARET
EXCLAMATION  --  MARK AND CARET

improperly escaped `!`:
EXCLAMATION  --  MARK AND CARET ^ SYMBOL
EXCLAMATION  --  MARK AND CARET ^ SYMBOL
EXCLAMATION  --  MARK AND CARET ^ SYMBOL
Run Code Online (Sandbox Code Playgroud)

在这里,我希望将整个字符串分成两个标记:EXCLAMATIONMARK AND CARET ^ SYMBOL.但是第二个标记太短,从插入符号开始的一切都^丢失了; 所以我得出结论,^用于逃避的!是字面意思.没有(或差)逃逸,返回的令牌是预期的.

jeb*_*jeb 6

选项FOR,IF并且REM仅解析为特殊字符阶段.
或者更好的是在特殊字符阶段检测到命令,然后激活不同的解析器.

因此,在选项中不可能使用延迟扩展或FOR-Parameter.

这些测试失败并出现错误

for /F %%O in ("defined") do (
  if %%O var echo yes
)

set option=defined
if !option! var echo yes
Run Code Online (Sandbox Code Playgroud)

这似乎是工作,但使用了错误的分隔符(D,e,i,l,m,s,y!)

set "myDelims=123"
for /F "tokens=1,2 delims=!myDelims!" %%A in ("Hello 1 world") do (
    echo Token1=%%A  Token2=%%B
)
Run Code Online (Sandbox Code Playgroud)

对于REM

set "help=/?"
REM !HELP!  - No help will be shown
Run Code Online (Sandbox Code Playgroud)

  • +1,左边未说明是你逃脱的明显事实!防止延迟扩张.如果延迟扩展永远不能用于特定的构造,那么就没有什么可以逃脱的了. (2认同)
  • @aschipfl 是的,这同样适用于 `FOR` 变量,请参阅第一个示例。另外,你的第二部分是对的,我修复了`D` (2认同)