Skip to content

/flags/cmd/setlocal-enabledelayedexpansion

setlocal enabledelayedexpansion: why variables do not change inside a for loop

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
Command Prompt (cmd)
command
setlocal
flag
enabledelayedexpansion
default
disabled (unless cmd /v:on or the DelayedExpansion registry value enables it)

cmd replaces %VAR% with its value when it reads a line, and a for loop or if (...) block is read as one line. A variable changed inside the block therefore still shows its old value there. setlocal enabledelayedexpansion turns on a second syntax, !VAR!, that is expanded each time the command runs. Use !VAR! for variables you change inside a block.

The difference

cmd
set count=0
for %%i in (a b c) do (
  set /a count+=1
  echo percent: %count%
)
echo after the loop: %count%
setlocal enabledelayedexpansion
set count=0
for %%i in (a b c) do (
  set /a count+=1
  echo delayed: !count!
)
endlocal

Output

percent: 0
percent: 0
percent: 0
after the loop: 3
delayed: 1
delayed: 2
delayed: 3

The variable does change inside the first loop; %count% was replaced by 0 before the loop started.

How setlocal works

  • setlocal starts a local environment: every set after it is undone at endlocal or when the batch file ends.
  • setlocal enabledelayedexpansion does the same and enables !VAR! until endlocal. setlocal disabledelayedexpansion turns it off again.
  • cmd /v:on enables delayed expansion for a whole cmd session, which is how it works at the interactive prompt.

To keep a value after endlocal, use the fact that %VAR% on the same line is expanded before endlocal runs:

cmd
setlocal enabledelayedexpansion
set "result=computed inside"
endlocal & set "result=%result%"
echo %result%

Output

computed inside

Exclamation marks in data

With delayed expansion on, ! is special everywhere in the line, so text and file names that contain ! lose it:

cmd
setlocal enabledelayedexpansion
echo Hello world!
setlocal disabledelayedexpansion
echo Hello world!

Output

Hello world
Hello world!

Enable it only around the code that needs it, or turn it off while you process file names that may contain !.

Sources