Delayed Environment Variable Expansion


Someone around the office joked that my blog is turning into “Random Batch File Tips and Tricks.” I’ll continue that trend with this post…

I recently encountered an environment variable surrounded by exclamation points instead of the usual percent signs. (ie. !MYENVVAR! instead of %MYENVVAR%). It turns out that this is used to delay the expansion of the environment variable until it is actually needed. The first processing step that happens in a statement is to expand all the environment variables.

I was in the process of writing up a big example when I found one on microsoft.com already. Scroll down and click on “What is delayed environment variable expansion?”

http://www.microsoft.com/technet/prodtechnol/Windows2000serv/support/FAQW2KCP.mspx

[UPDATE] Looks like that link is dead, but you can find it on archive.org: http://web.archive.org/web/20080430040305/http://www.microsoft.com/technet/prodtechnol/Windows2000serv/support/FAQW2KCP.mspx I’ll copy it here too just in case that archived version disappears:

The default behavior of the CMD command processor is to evaluate an environment variable once per statement execution.

To demonstrate this behavior, assume that your c:\test folder has 3 files:

File1.txt

File2.txt

File3.txt

If you run a batch script containing:

@echo off 
cd /d c:\test 
@echo on 
set LIST= 
for %%i in (*) do set LIST=%LIST% %%i 
echo %LIST%

You would see the following:

C:\TEST\>set LIST=

C:\TEST\>for %i in (*) do set LIST= %i

C:\TEST\>set LIST= File1.txt

C:\TEST\>set LIST= File2.txt

C:\TEST\>set LIST= File3.txt

C:\TEST\>echo File3.txt

C:\TEST\> File3.txt

You can see from this example that the LIST variable is expanded just once when the FOR statement is executed. Since LIST was empty, only the last file found is set into the variable.

Windows 2000 supports delayed environment variable expansion. You must enable delayed environment variable expansion for the CMD session by using the CMD /V:ON switch, or by issuing a setlocal ENABLEDELAYEDEXPANSION command.

With delayed environment variable expansion enabled, you can use the ! instead of the %, as follows:

@echo off 
cd /d c:\test 
@echo on 
set LIST= 
for %%i in (*) do set LIST=!LIST! %%i 
echo %LIST%

This yields the following output:

C:\TEST\>set LIST=

C:\TEST\>for %i in (*) do set LIST=!LIST! %i

C:\TEST\>set LIST=!LIST! File1.txt

C:\TEST\>set LIST=!LIST! File2.txt

C:\TEST\>set LIST=!LIST! File3.txt

C:\TEST\>echo File1.txt File2.txt File3.txt

C:\TEST\> File1.txt File2.txt File3.txt

You can have delayed environment variable expansion enabled by default, if you use Regedt32 to navigate to either of the following keys:

HKEY_LOCAL_MACHINE \Software \Microsoft \Command Processor 
HKEY_CURRENT_USER \Software \Microsoft \Command Processor

On the Edit menu, Add Value name DelayedExpansion, as a REG_DWORD data type. A data value of 1 enables delayed environment variable expansion and a data value of 0 disables it. Invoking the /V:ON or /V:OFF switch on CMD.EXE and/or using the setlocal ENABLEDELAYEDEXPANSION command, overrides the registry setting for the CMD session.


Comments (6)

  1. Anonymous says:

    I am setting the environment variable within an application called by the batch file. I then want to display the contents of the environment variable. Even with delayed expansion – I don’t seem to be getting the current value of the environment variable – but rather the value set when the batch file is first read.

    Here is my code:

    CALL myApp.exe /RunSilent

    echo !Test!

    (Test is set within the application as a user-level environment variable.)

    If I check the registry, Test always returns what is there before the batch file was executed, not after the myApp.exe was run.

    Any tips?

  2. Anonymous says:

    cmd.exe needs to be invoked with the /v argument.

  3. Anonymous says:

    Or use "setlocal enabledelayedexpansion" in the batch file.  

  4. Anonymous says:

    If you want to echo a "!" when delayed expansion is on, I found it has to be escaped twice when in a batch file.  It’s like the % (command-line) % and %% (batch) variable namining in FOR statements.  

    Command-line:

     echo WARNING^!  Error !ERRORLEVEL! detected…  

    Batch:

     echo WARNING^^!  Error !ERRORLEVEL! detected…  

  5. Anonymous says:

    Thanks, Randy!  I was frustrated by this until I read your response.  Works great.  Opens up a whole new world of batch possibilities….

  6. Nayana says:

    I think the page you pointed by the link, resting in the heaven