Skip to main content

How CALL SET Expands Variables in Batch Script

In advanced batch scripting, you may encounter a situation where a variable doesn't hold a direct value, but instead holds the name of another variable. To get the final value, you need to perform a "double expansion" or "indirect expansion." This is a tricky problem because the standard %VAR% syntax is evaluated only once.

The classic, powerful technique to solve this is the CALL SET command. It leverages a special behavior of the CALL command to force a second round of parsing on a line, allowing nested variables to be resolved. This guide will break down how this works, why it's necessary, and compare it to the modern alternative, delayed expansion.

The Core Problem: Indirect or Nested Variable Expansion

Let's say you have two variables. One is a "pointer" to the other.

@ECHO OFF
SET "MyValue=Hello World"
SET "Pointer=MyValue"

Your goal is to get "Hello World" by starting with the Pointer variable. A standard ECHO fails:

ECHO %Pointer%

Output:

MyValue

You need the script to first evaluate %Pointer% to get MyValue, and then evaluate %MyValue% to get Hello World. This is a two-step expansion.

The Magic of CALL: Forcing a Second Parsing Pass

The CALL command is typically used to run another batch script or a subroutine. However, it has a special property: it forces the command processor to evaluate the command line in two passes.

  1. Parsing Pass: Before CALL executes, the command processor parses the line and expands all %VAR% variables.
  2. Execution Pass: CALL then takes the result of the first pass and executes it as a new command, which triggers a second round of parsing and expansion.

Let's see this with ECHO:

@ECHO OFF
SET "MyValue=Hello World"
SET "Pointer=MyValue"

CALL ECHO %%%Pointer%%%

Output:

Hello World

How it works:

  1. Parsing Pass: cmd.exe sees CALL ECHO %%%Pointer%%%. It expands %Pointer% to MyValue and resolves the paired percent signs %% into a single %. The command becomes: CALL ECHO %MyValue%.
  2. Execution Pass: CALL now executes the command ECHO %MyValue%. This new command is parsed, %MyValue% is expanded to Hello World, and the ECHO command prints the final result.

The CALL SET Trick Explained

While CALL ECHO works for displaying, you often need to store the result in a variable. This is where CALL SET comes in. The logic is exactly the same.

Syntax: CALL SET "ResultVar=%%%PointerVar%%%"

@ECHO OFF
SET "MyValue=Hello World"
SET "Pointer=MyValue"
SET "Result="

ECHO Before: Result is "%Result%"

CALL SET "Result=%%%Pointer%%%"

ECHO After: Result is "%Result%"

How it works:

  1. Parsing Pass: The line CALL SET "Result=%%%Pointer%%%" becomes CALL SET "Result=%MyValue%".
  2. Execution Pass: CALL now executes the command SET "Result=%MyValue%". This is parsed, %MyValue% becomes Hello World, and the SET command assigns this value to the Result variable.

Basic Example: Resolving a Pointer

This script puts the full CALL SET logic into a clear demonstration.

@ECHO OFF
SETLOCAL

SET "ConfigValue_A=Production"
SET "ActiveConfig=ConfigValue_A"
SET "FinalValue="

ECHO The pointer 'ActiveConfig' points to the variable named: %ActiveConfig%
ECHO.

ECHO --- Using CALL SET to resolve the pointer ---
CALL SET "FinalValue=%%%ActiveConfig%%%"

ECHO The final resolved value is: %FinalValue%

ENDLOCAL

Output:

The pointer 'ActiveConfig' points to the variable named: ConfigValue_A

--- Using CALL SET to resolve the pointer ---
The final resolved value is: Production

CALL SET vs. Delayed Expansion (The Modern Way)

For this same problem, the modern solution is Delayed Expansion. It's often easier to read and understand.

The Logic: SETLOCAL ENABLEDELAYEDEXPANSION allows you to use !VAR! for dynamic expansion. When the parser sees !%Pointer%!, it first expands the %PERCENT% variable (static pass) and then the !EXCLAMATION! variable (dynamic pass).

An example of script using delayed expansion:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

SET "MyValue=Hello World"
SET "Pointer=MyValue"

SET "Result=!%Pointer%!"

ECHO The result with Delayed Expansion is: !Result!

How it works:

  1. The parser sees !%Pointer%!.
  2. It expands the "inner" percent variable first: %Pointer% becomes MyValue.
  3. The string is now !MyValue!.
  4. Delayed expansion then expands !MyValue! to Hello World.

For most new scripts, delayed expansion is the preferred method as its syntax is cleaner. However, CALL SET is a powerful technique that works without needing to enable delayed expansion.

Common Pitfalls and How to Solve Them

  • Confusing Percent Signs: The syntax %%%Pointer%%% is cryptic. It's easy to get the number of percent signs wrong. Just remember that it's %% (to escape a %) followed by %Pointer% followed by another %%.
  • Special Characters: If your final value contains special characters like &, |, or >, using CALL ECHO will fail. CALL SET "Result=..." is safer because the SET "Var=Value" syntax is robust and treats the value as a literal string.

Practical Example: Accessing Dynamic Variables in a Loop

This is where CALL SET shines. The script creates several variables (VAR1, VAR2, VAR3) and then uses a FOR loop to access them dynamically by number.

@ECHO OFF
SETLOCAL
SET "VAR1=Apple"
SET "VAR2=Banana"
SET "VAR3=Cherry"

ECHO --- Looping through dynamic variables ---
FOR /L %%i IN (1,1,3) DO (
CALL SET "CurrentValue=%%VAR%%i%%"

REM We need delayed expansion here to ECHO the changing 'CurrentValue'
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO The value of VAR%%i is: !CurrentValue!
ENDLOCAL
)

Output:

--- Looping through dynamic variables ---
The value of VAR1 is: Apple
The value of VAR2 is: Banana
The value of VAR3 is: Cherry

Conclusion

CALL SET is an advanced batch scripting technique that solves the problem of nested or indirect variable expansion by forcing the command processor to parse a line twice.

  • It is the classic method for resolving a variable whose name is stored in another variable.
  • The key syntax is CALL SET "Result=%%%Pointer%%%".
  • While powerful, it is often considered less readable than its modern alternative, Delayed Expansion (!%Pointer%!).

Understanding CALL SET is a sign of mastering the intricacies of the cmd.exe parser and is an invaluable tool for certain complex scripting challenges.