When delayed expansion is enabled, it is (usually) necessary to escape exclamation marks properly in order to get literal ones (like ^^! or, when being in between "", ^!, in order to get a literal !).
However, why is no escaping of exclamation marks necessary but even distructive within the parameters provided immediately after the /R or the /F switch of the for loop command?
With the aforementioned escaping rules in mind, I created the following batch script using for /R and as a parameter a directory with an ! in its name (stated after the /R option):
@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
Surprisingly, the directory is enumerated only without escaping the !. This is an output:
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
I expect the directory to be enumerated in case the exclamation mark is in fact properly escaped, otherwise not; but the result shows the opposite behaviour.
A similar phenomenon arises with for /F, like in the following script with an ! in the option string:
@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
The output looks like this, strangely:
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
Here I expect the whole string to be split into exactly two tokens: EXCLAMATION and MARK AND CARET ^ SYMBOL. But the second token is too short, everything beginning from the caret symbol ^ is missing; so I conclude that the ^ used for escaping of the ! is taken literally. With no (or poor) escaping, the returned tokens are the intended ones.
The options of FOR, IF and REM are only parsed up to the special character phase.
Or better the commands are detected in the special character phase and a different parser is activated then.
Therefore it's not possible to use delayed expansion nor FOR-Parameter in the options.
These tests fail with errors
for /F %%O in ("defined") do (
if %%O var echo yes
)
set option=defined
if !option! var echo yes
This seems to work, but uses the wrong delimiters (D, e, i, l, m, s, y and ! )
set "myDelims=123"
for /F "tokens=1,2 delims=!myDelims!" %%A in ("Hello 1 world") do (
echo Token1=%%A Token2=%%B
)
And for REM
set "help=/?"
REM !HELP! - No help will be shown
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With