0

I already asked this in SS64.

I'm trying to use IF DEFINED to control branching, but it doesn't work. I will start with an example

setLocal
if defined _var (
::  _var is NOT defined, so the next line should not be executed
    if %_var%=="test" echo %_var% is defined
)
endLocal

Processing stops when it hits the ECHO statement and exits with "echo was unexpected at this time." error message. The problem is that the nested IF statement is being executed, and it shouldn't be because _var is not defined. How can I fix this?

Thanks, Shane.

Shane Goodman
  • 719
  • 1
  • 6
  • 16
  • Because using `::` for a comment can only be done outside of a code block defined by parenthesis. Instead use `REM `. `::` is an over-loaded label that will not print even if you leave `ECHO ON` but it does not play nice with parenthesis, and the script will throw errors snd evaluate lines it should not be able to reach that are inside parenthesis when `::` is present – Ben Personick Nov 12 '19 at 13:19

2 Answers2

1

The real problem you have is just that you did not match the if statement correctly. To explain, see the following example.

if a == "a"..

vs

if "a"=="a"..

So in you example you were trying to match:

if test=="test"..

The reason we use double quotes on both sides of the variable is to ensure we eliminate any possible whitespace which might become part of the values you are testing.

So this version should be fine. I added the /i switch to match test case insensitive, so it will match test in any case combination. TesT, tEst, TEST, test etc.:

setLocal
set /p "_var=Enter Variable name: "
set "_var=%_var:"=%"
if defined _var (
:# rem _var is NOT defined, so the next line should not be executed
    if /i "%_var%"=="test" echo %_var% is defined
)
endLocal
Gerhard
  • 22,678
  • 7
  • 27
  • 43
1

There are multiple issues with this small code block.

The first issue is using an invalid label with :: to write a comment inside a command block starting with ( and ending matching with ). A label is not allowed inside a command block. A line starting with : after 0 or more spaces/tabs results in undefined behavior on execution of the command block. Undefined behavior means it can work by chance, but it can also fail by chance. The execution behavior is undefined.

The command for a comment is REM. Run in a command prompt window rem /? for help on this command.

Please note that REM is a command and for that reason a comment line with REM is parsed by Windows command processor like any other command line which is important on comment line contains %variable% or operators like &|<>. So be careful on what is written in comment text.


The second issue is that the comment itself is not correct.

_var is NOT defined, so the next line should not be executed

This comment is wrong because the environment variable _var is definitely defined on cmd.exe reaching this line after execution of the command if defined _var with a positive result.


The third issue is that if %_var%=="test" is syntactically not 100% correct. There are missing the spaces around the comparison operator ==. But this syntax error is automatically detected by cmd.exe on processing the batch file with automatic correction by inserting the two missing spaces around operator ==. This can be seen on debugging the batch file.


The fourth issue is that IF compares two strings always with including the double quotes on string comparison. So on usage of if %_var%=="test" or syntactically correct if %var% == "test" the condition is only true if assigned to environment variable _var is the string "test" with the double quotes. This string comparison evaluates to false in all other cases on _var defined if not resulting in a syntax error before, see next issue.

Please read my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files explaining very detailed how a string comparison is done by command IF.


The fifth issue is that Windows command processor parses the entire command block starting with ( and ending with matching ) before executing the command IF.

The inner IF without environment variable _var defined results on parsing the command block in the line:

if =="test" echo is defined

cmd.exe detects a serious syntax error on this command line and exits batch file processing because of this syntax error before executing the outer IF condition.

One solution would be enclosing %var% in double quotes, i.e. use in batch file:

if "%_var%" == "test" echo %_var% is defined

This IF command line is correct if the environment variable _var is not defined on parsing the command block.

But this solution does not work if _var is defined and contains one or more " because of the double quotes result again in an invalid syntax for command line with command IF.

Further echo does not result in printing the value of the environment variable _var if the string assigned to the environment variable contains one of these characters &|<>. Well, echo is not executed in this case as the string comparison evaluates to false.

Therefore a better solution is enabling delayed expansion and referencing the value of environment variable _var with delayed environment variable expansion to avoid that the value of this environment variable modifies the command line to execute by cmd.exe after parsing entire command block.

setlocal EnableExtensions EnableDelayedExpansion
if defined _var (
    rem  _var is defined, so the next line should be executed.
    if "!_var!" == "test" echo _var is defined with !_var!.
)
endlocal

The command extensions must be also enabled because otherwise if defined would not be supported by command IF. The command extensions are by default enabled, but it is better to write a batch file with no dependency on settings or defaults defined outside of the batch file as far as possible.

Well, the echo command could be also written with:

echo _var is defined with test.

It is not possible that echo prints here something other than test on using !_var!.

Another solution is to avoid the usage of a command block and make sure that the string value assigned to environment variable _var does not contain any " and output the value of this environment variable enclosed in double quotes to avoid that &|<> affect the execution of echo command line.

setlocal EnableExtensions DisableDelayedExpansion
if not defined _var goto Label

rem _var is defined, so the next line should be executed.
rem Remove all double quotes from string value of environment variable.
rem It is important to use double quotes around argument string of the
rem command SET as otherwise a string with "&|<>" would be not correct
rem assigned to the environment variable.

set "_var=%_var:"=%"

rem It could happen that _var is not defined anymore. But in this case
rem it is no problem anymore that cmd.exe replaces all %var% by nothing
rem on entire command line before executing the IF condition as the
rem remaining commands on this command line with enclosing the string
rem value of _var in double quotes is always syntactically correct even
rem on string value containing one or more of the operators "&|<>".

if /I "%_var%" == "test" echo _var is defined with "%_var%".

:Label
endlocal

The string comparison is done here case-insensitive.

See also:

It must be always made sure that a user input string or a string passed to the batch file as argument or a string determined by the batch file itself for example from a file/folder name/path is enclosed in double quotes and does itself not contain double quotes for being correct processed by the batch file. While the characters |<> are not allowed in a file/folder name, & is allowed in a file/folder name and results in many batch files on being interpreted as AND operator on file/folder string not enclosed in double quotes, even on simply echoing the file/folder name.

See also the Microsoft documentation page Naming Files, Paths, and Namespaces.


It is very important to understand on writing a batch file How does the Windows Command Interpreter (CMD.EXE) parse scripts? It must be always taken into account how a command line finally looks on execution with environment variable references expanded during parsing phase of a command line or an entire command block. The finally executed command line after parsing must be of valid syntax or cmd.exe exits batch file processing.

Mofi
  • 46,139
  • 17
  • 80
  • 143