Dynamic vs. Static Variable Expansion in Batch Script
One of the most confusing yet powerful concepts in advanced batch scripting is the distinction between two types of variable expansion: static expansion (using %PERCENT% signs) and dynamic expansion, better known as Delayed Expansion (using !EXCLAMATION! marks). Understanding this difference is absolutely essential for writing any script that involves changing a variable's value inside a loop.
This guide will explain what static and dynamic expansion are, demonstrate the classic problem that static expansion causes inside a loop, and show you how delayed expansion is the definitive solution to this problem.
Static Expansion: The %PERCENT% Method
Static expansion is the standard method of variable expansion that everyone learns first. When you use percent signs around a variable name (%MyVar%), you are using static expansion.
The term "static" refers to when the variable is replaced with its value.
The Core Problem: How cmd.exe Parses Code Blocks
The entire issue stems from the way the command interpreter (cmd.exe) processes commands. When cmd.exe encounters a command block (a single command or a multi-line block enclosed in parentheses), it reads the entire block at once and replaces all %PERCENT% variables with their values before any of the commands in the block are executed.
Consider this simple block:
SET MyVar=Hello
(
ECHO The value is: %MyVar%
SET MyVar=Goodbye
ECHO The value is now: %MyVar%
)
Before the first ECHO is ever run, cmd.exe reads the whole (...) block and does its substitutions. It sees that %MyVar% is "Hello", so the block effectively becomes this in memory:
(
ECHO The value is: Hello
SET MyVar=Goodbye
ECHO The value is now: Hello
)
When this pre-processed block is executed, it will print "Hello" both times, which is not what the programmer intended.
The Classic "Loop Counter" Failure
This parsing behavior causes the most common and frustrating bug for new batch scripters: a counter that doesn't count inside a FOR loop.
An example of Script using static %PERCENT% expansion:
@ECHO OFF
SET count=0
FOR /L %%i IN (1,1,3) DO (
SET /A "count+=1"
ECHO The count is: %count%
)
ECHO The final count is: %count%
Output:
The count is: 0
The count is: 0
The count is: 0
The final count is: 3
This is the classic symptom of a static expansion problem. The FOR loop's (...) block was parsed once at the beginning. At that time, %count% was 0, so the ECHO command was fixed to ECHO The count is: 0 for all three iterations. The SET /A command is working, but its updated value is never read inside the loop.
The Solution - Dynamic Expansion: DelayedExpansion
To solve this, we need a way to tell the command processor, "Don't replace this variable now; wait until the command is actually about to run." This is dynamic or delayed expansion.
When delayed expansion is enabled, you use exclamation marks instead of percent signs: !MyVar!.
When cmd.exe parses a block, it leaves !VARIABLES! alone. Then, as it executes each command line by line, it expands the !VARIABLES! to their current, live value at that exact moment.
How to Enable and Use Delayed Expansion
There are two steps to using delayed expansion:
- Enable it: You must run the command
SETLOCAL ENABLEDELAYEDEXPANSIONat the beginning of your script (or before the code block where you need it). - Use it: Use exclamation marks instead of percent signs to access variables that change within a code block:
!MyVar!.
The "Loop Counter" Fixed with Delayed Expansion
Let's fix our broken counter script from previous section.
An example of script using dynamic !EXCLAMATION! expansion:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET count=0
FOR /L %%i IN (1,1,3) DO (
SET /A "count+=1"
ECHO The count is: !count!
)
ECHO The final count is: !count!
Output:
The count is: 1
The count is: 2
The count is: 3
The final count is: 3
This now works exactly as intended. On each iteration, the ECHO command is executed, and only then does cmd.exe look for the current value of !count!, which has been correctly updated by the SET /A command on the line before.
Conclusion
Understanding the difference between static and dynamic variable expansion is the key that unlocks the ability to write complex, powerful batch scripts.
-
Static Expansion (
%MyVar%):- Is expanded once, when a code block is first parsed.
- It sees the value of the variable as it was before the block began.
- This is fine for simple scripts but will fail for variables that change inside a loop.
-
Dynamic/Delayed Expansion (
!MyVar!):- Is enabled with
SETLOCAL ENABLEDELAYEDEXPANSION. - Is expanded at execution time, line by line.
- It sees the current, up-to-date value of a variable inside a code block.
- This is essential for any
FORloop where you need to read and write to the same variable.
- Is enabled with
Rule of Thumb: If you are in a FOR loop and you are changing a variable that you also need to read in that same loop, you must use delayed expansion.