Friday, April 30, 2010

Delayed environment variable expansion in Windows

Today I got to learn the wonders of "Delayed Environment Variable Expansion" in windows.... this one really hurt my head :)

As a basis for this entry I'm going to use information from another article as the starting point (http://stackoverflow.com/questions/305605/weird-scope-issue-in-bat-file). After I read the article for the first time I still wasn't sure what was going on so I'm going to explain in a little more detail below.

For the example we'll start with the code below:

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" @echo If you see this, it worked
)

When I first looked at the code I throught that I would see "If you see this, it worked" in the command prompt window when the code is ran, but unfrotunately that is not how bat files work... (this really surprised me).

What is really happening is as follows:

1) Windows loads "before" into the variable
2) Windows loads the entire if into memory
3) Windows sets all instances of %VAR% to before
4) when the code hits the following line "if "%VAR%" == "after" @echo If you see this, it worked" the value in %VAR% is "before" because the value was replaced prior to the "set VAR=after" statement and %VAR% will only be equal to "after" once the program exits the if statement's scope

To take this example one step further I added "@Echo %VAR%" at the end of the code (after the if statement) as described below.

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" @echo If you see this, it worked
)
@Echo %VAR%


When the code above executes you get the following output to the command window:

if "before" == "before" (
set VAR=after
if "before" == "after"
)
after

Please note that once the program gets out of the if statement scope the value in %VAR% IS set to "after".


From the articles I've read thus far the system works like this because this is how it was originally implemented. However, there is something called "delayed environment variable expansion" in windows that makes the command file work as we expect. The code with delayed environment variable expansion implemented is provided below:

SETLOCAL EnableDelayedExpansion
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo If you see this, it worked
)
@Echo %VAR%


The most important difference with the code provided above is the statement "SETLOCAL EnableDelayedExpansion". When this is set all variables marked with '!' instead of '%' are evaluated at runtime when they are used rather than when the block of code they are in is evaluated.

So with that being said, when the code above is executed you'll get the following output.

set VAR=after
if "!VAR!" == "after"
)
If you see this, it worked
after

No comments:

Post a Comment