Why IF checking ignores “^some_word” aft

2019-03-21 16:02发布

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 #

2条回答
劫难
2楼-- · 2019-03-21 16:19

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.

查看更多
Viruses.
3楼-- · 2019-03-21 16:37

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
查看更多
登录 后发表回答