Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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:

lolwut

share|improve this question
    
I screwed up my first answer by accidentally throwing i1, i2, e1, and e2 into my environment. My new answer should be what you're looking for! –  Stephen Jennings Aug 31 '11 at 4:01

3 Answers 3

up vote 5 down vote accepted

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
share|improve this answer
    
Exactly my thoughts. Delayed expansion. –  surfasb Aug 31 '11 at 4:21
    
N.B. "goto :EOF" is equivalent to "go to the end of the file." It's also used to "return" from batch "subroutines," as in "call :mySub". –  BillP3rd Aug 31 '11 at 4:58
1  
@BillP3rd: Nice! I tend to use Exit /B for the same purpose. –  Hand-E-Food Aug 31 '11 at 6:11
    
And I learned something too... Your method can set ERRORLEVEL. Cool! –  BillP3rd Aug 31 '11 at 6:29
    
Brilliant! How could one ever know something like this? –  oscilatingcretin Aug 31 '11 at 12:31

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!

share|improve this answer

the whole cmd stuff works more like a preprocessor which runs the file once, creates the preprocessed 'real' file and then runs that. this means that cmd does not (re)evaluate dynamically set variables (or for loops based upon the changing content of a 'variable'.

since your description of the problem is a bit vague: do you use variables instead of the strings 'prod' and 'xps' and expect dynamic interpretation of these 'variables'?

share|improve this answer
    
Actually, batch files are not "preprocessed" all at once. They're read and processed one line at a time. This is easy to demonstrate. Where delayed expansion comes into play is where multiple lines are read as a single line, as in: if 1==1 (foo & bar & baz). The contents of the () will be read at once but nothing following. –  BillP3rd Aug 31 '11 at 4:55
    
@BillP3rd: but the effect for 'for'-loops is as IF it would be preprocessed and a gigantic preprocessed batch file (then one line at a time) is executed. –  akira Aug 31 '11 at 5:49
    
@akira, I think what you mean is each line and bracketted block is preproccessed. With matching brackets at the start and end, then yes, it preprocesses the entire file. –  Hand-E-Food Aug 31 '11 at 22:50

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.