Here's the example:
@echo off
:: check syntax ignores "^<delimiter>something" right after check expression
if defined path^ blah echo #
if exist c:\^ blah echo #
if errorlevel 0^ blah echo #
:: but in comparison operations escaping works well
if 1^ blah==1^ blah echo #
Because that is how it works ;-)
In my opinion it is a design flaw with the IF command. The IF command has complicated (multiple phase) parsing rules, and I think MicroSoft dropped the ball a bit.
Escape does not work with IF EXIST or IF DEFINED. The first parsing pass properly identifies the condition and command, but the actual comparison stops at the first white space.
Quotes work with IF EXIST. But quotes do not help with IF DEFINED because they are considered to be part of the variable name.
The only way to get it to work properly in all cases is to use a FOR variable or delayed expansion:
@echo off
setlocal enableDelayedExpansion
set "var=%%%%~A"
:: Start with a clean slate
set "a="
set "a z="
del "a" "a z" 2>nul
:: ---------- test escape --------------
:: This fails
set "a z=1"
if defined a^ z (
echo if defined a^^ z TRUE = SUCCESS
) else (
echo if defined a^^ z FALSE = FAILURE
)
set "a z="
:: This fails
copy nul "a z" >nul
if exist a^ z (
echo if exist a^^ z TRUE = SUCCESS
) else (
echo if exist a^^ z FALSE = FAILURE
)
del "a z"
:: This fails (looks at the wrong variable)
set "a=1"
if defined a^ z (
echo if defined a^^ z TRUE = FAILURE
) else (
echo if defined a^^ z FALSE = SUCCESS
)
set "a="
:: This fails (looks at wrong file)
copy nul "a" >nul
if exist a^ z (
echo if exist a^^ z TRUE = FAILURE
) else (
echo if exist a^^ z FALSE = SUCCESS
)
del "a"
echo(
:: ---------- test quotes --------------
:: This fails
set "a z=1"
if defined "a z" (
echo if defined "a z" TRUE = SUCCESS
) else (
echo if defined "a z" FALSE = FAILURE
)
set "a z="
:: This succeeds
copy nul "a z" >nul
if exist "a z" (
echo if exist "a z" TRUE = SUCCESS
) else (
echo if exist "a z" FALSE = FAILURE
)
del "a z"
:: This fails (looks at the wrong variable with quotes in name)
set ""a z"=1"
if defined "a z" (
echo if defined "a z" TRUE = FAILURE
) else (
echo if defined "a z" FALSE = SUCCESS
)
set ""a z"="
:: This succeeds
copy nul "a" >nul
if exist "a z" (
echo if exist "a z" TRUE = FAILURE
) else (
echo if exist "a z" FALSE = SUCCESS
)
del "a"
echo(
:: ---------- test FOR variable --------------
:: This succeeds
set "a z=1"
for %%A in ("a z") do if defined %%~A (
echo if defined !var! TRUE = SUCCESS
) else (
echo if defined !var! FALSE = FAILURE
)
set "a z="
:: This succeeds
copy nul "a z" >nul
for %%A in ("a z") do if exist %%~A (
echo if exist !var! TRUE = SUCCESS
) else (
echo if exist !var! FALSE = FAILURE
)
del "a z"
:: This succeeds
set "a=1"
for %%A in ("a z") do if defined %%~A (
echo if defined !var! TRUE = FAILURE
) else (
echo if defined !var! FALSE = SUCCESS
)
set "a="
:: This succeeds
copy nul "a" >nul
for %%A in ("a z") do if exist %%~A (
echo if exist !var! TRUE = FAILURE
) else (
echo if exist !var! FALSE = SUCCESS
)
del "a"
echo(
:: ---------- test delayed expansion --------------
set "var=a z"
:: This succeeds
set "a z=1"
if defined !var! (
echo if defined ^^!var^^! TRUE = SUCCESS
) else (
echo if defined ^^!var^^! FALSE = FAILURE
)
set "a z="
:: This succeeds
copy nul "a z" >nul
if exist !var! (
echo if exist ^^!var^^! TRUE = SUCCESS
) else (
echo if exist ^^!var^^! FALSE = FAILURE
)
del "a z"
:: This succeeds
set "a=1"
if defined !var! (
echo if defined ^^!var^^! TRUE = FAILURE
) else (
echo if defined ^^!var^^! FALSE = SUCCESS
)
set "a="
:: This succeeds
copy nul "a" >nul
if exist !var! (
echo if exist ^^!var^^! TRUE = FAILURE
) else (
echo if exist ^^!var^^! FALSE = SUCCESS
)
del "a"
--OUTPUT--
if defined a^ z FALSE = FAILURE
if exist a^ z FALSE = FAILURE
if defined a^ z TRUE = FAILURE
if exist a^ z TRUE = FAILURE
if defined "a z" FALSE = FAILURE
if exist "a z" TRUE = SUCCESS
if defined "a z" TRUE = FAILURE
if exist "a z" FALSE = SUCCESS
if defined %%~A TRUE = SUCCESS
if exist %%~A TRUE = SUCCESS
if defined %%~A FALSE = SUCCESS
if exist %%~A FALSE = SUCCESS
if defined !var! TRUE = SUCCESS
if exist !var! TRUE = SUCCESS
if defined !var! FALSE = SUCCESS
if exist !var! FALSE = SUCCESS
Why?
I don't know.
But It seems that the tokenizer executes two times in this cases.
Here is an example to show that the tokenizer is independent of the interim output of ECHO ON
echo ON
setlocal EnableDelayedExpansion
set "var=hello you"
if !var! == hello^ you echo true
if !var! == hello you echo true
Both lines are shown as if !var! == hello you echo true
but only the first executes the echo true
, as there hello you
is one token but not in the second sample.
set var=1
IF defined var^ bang echo VAR is defined
But as dbenham explains, the IF DEFINED
and the If EXIST
seems to tokenize it first as expected, as the second word will not be used as a command, but it's completly dropped.
The interessting effect is that a FOR parameter and delayed expansion aren't affected, although these phases are following directly the echo phase.
So I assume, that there is used another tokenizer by IF defined
and IF EXIST
in contrast to IF .. <compare>
.
With ECHO ON
(as JosefZ mentioned) you can see the differences.
if !var! == hello^ you echo true
if defined hello^ you echo is defined
Output
C:\temp>if !var! == hello you echo true
C:\temp>if defined hello echo is defined
In the first case the hello you
is complete, but in the second sample the you
was dropped.