Windows 7 batch file: Why are these IF blocks keeping me from assigning variables to a value?
Here's the broken batch file:
@echo off
if prod==prod (
if xps==xps (
set i1=prodxpsi1
set i2=prodxpsi2
set e1=prodxpse1
set e2=prodxpse2
) else (
set i1=prodzpsi1
set i2=prodzpsi2
set e1=prodzpse1
set e2=prodzpse2
)
if 1==1 (
echo %i1%, %i2%, %e1%, %e2%
) else (
echo %i1%, %i2%, %e1%, %e2%
)
)
pause
However, when I take out the outer if prod==prod
block like this, it works:
@echo off
if xps==xps (
set i1=prodxpsi1
set i2=prodxpsi2
set e1=prodxpse1
set e2=prodxpse2
) else (
set i1=prodzpsi1
set i2=prodzpsi2
set e1=prodzpse1
set e2=prodzpse2
)
if 1==1 (
echo %i1%, %i2%, %e1%, %e2%
) else (
echo %i1%, %i2%, %e1%, %e2%
)
pause
When I run the batch file the first time, it echoes , , ,
. When I run it the second time, it works fine:
Solution 1:
This is a quirk of the command parser. Because of the brackets, it sees everything from if ...
to )
as one line. When it reads this "one line", it expands all the variables to their values before processing any of it. The set
commands all occcur after the variables have been expanded.
There are two solutions: branches and delayed exansion.
Branches: Ensure the set
commands and echo
commands are not in the same set of top-most brackets:
@echo off
if not prod==prod goto :end
if xps==xps (
set ...
) else (
set ...
)
if 1==1 (
...
)
:end
pause
Delayed Expansion: This causes variables to be expanded as needed, rather than in advance. Use the command SetLocal EnableDelayedExpansion
to activate this mode, use ! marks to refer to a variable in this manner, and use the command EndLocal
when you're done. Note that EndLocal
will forget any variables declared after SetLocal
, so you may want to move SetLocal
to after the set
commands.
@echo off
setlocal enabledelayedexpansion
if prod==prod (
if xps==xps (
set i1=prodxpsi1
...
) else (
set i1=prodzpsi1
...
)
if 1==1 (
echo !i1!, !i2!, !e1!, !e2!
) else (
echo !i1!, !i2!, !e1!, !e2!
)
)
endlocal
pause
Solution 2:
If you enable delayed expansion, then use !var!
syntax to reference the variables, then it behaves as you expect.
@echo off
setlocal enabledelayedexpansion
if prod==prod (
if xps==xps (
set i1=prodxpsi1
set i2=prodxpsi2
set e1=prodxpse1
set e2=prodxpse2
) else (
set i1=prodzpsi1
set i2=prodzpsi2
set e1=prodzpse1
set e2=prodzpse2
)
if 1==1 (
echo !i1!, !i2!, !e1!, !e2!
) else (
echo !i1!, !i2!, !e1!, !e2!
)
)
The help text (cmd /?
) explains:
/V:ON -- Enable delayed environment variable expansion using ! as the delimiter. For example, /V:ON would allow !var! to expand the variable var at execution time. The var syntax expands variables at input time, which is quite a different thing when inside of a FOR loop.
If delayed environment variable expansion is enabled, then the exclamation character can be used to substitute the value of an environment variable at execution time.
When you wrap the whole thing in an if
statement, the whole block becomes essentially a single command. All the variables inside the command are expanded at the time of input, which is before the if xps==xps
part begins. At that point in the script, the variables have not yet been defined.
By using the !var!
syntax along with delayed expansion, then the value of !i1!
isn't evaluated until that particular line is executed.
Thanks! Your question taught me something today!