Example of delayed expansion in batch file

Look at the following examples...

Example 1: The following code DOESN'T use delayed expansion, so the variables in the for loop are expanded only one time. This means that %Count% will always expand to 0 in each iteration of the loop, no matter what we do to it with the set command:

@echo off
set COUNT=0

for %%v in (1 2 3 4) do (
  set /A COUNT=%COUNT% + 1
  echo Count = %COUNT%
)
pause

So this script will output:

Count = 0
Count = 0
Count = 0
Count = 0

This is not how this loop is supposed to work.

Example 2: On the other hand, if we use delayed expansion, we have the following script, which will run as expected.

setlocal ENABLEDELAYEDEXPANSION
set COUNT=0

for %%v in (1 2 3 4) do (
  set /A COUNT=!COUNT! + 1
  echo Count = !COUNT!
)

pause

and, as expected, it will output:

Count = 1
Count = 2
Count = 3
Count = 4

When you use the ENABLEDELAYEDEXPANSION, and expand a variable using ! instead of %, the variable is re-expanded each time, and everything works as it's supposed to.


I wanted to add a great example on how "EnableDelayedExpansion" (EDE) can be useful outside of the ubiquitous FOR loop examples.

Here is a line of earthquake data that I wish to parse (I call it it 1line.txt)

ak_11574812 2015.04.29.193822 62.9525 -148.8849 1.0 9.5 1 49km S of Cantwell, Alaska

The problem I ran into was that last segment of this line does not always start at the same column number. So I needed to create a flexible SET command that will accurately pluck out the last segment of this line.

ECHO OFF
setlocal enableDelayedExpansion
set where=72
set /p line=<1line.txt
set locate=!line:~%where%,28!
echo %locate%

EDE allows me to place a variable (where) inside another variable (line). EDE will translate the variable bracketed by % first, then process the variable bracketed by ! and (in this case) push out the results into the "locate" variable.


Max's answer gives an example of where a batch script would act differently with or without delayed expansion.

For the sake of completeness, let's answer another part of the question and show a situation where you would NOT want to use delayed expansion when your data contain an exclamation mark ! (and show two ways of processing such data):

@ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion

  set "_auxFile=%temp%\%~n0.txt"
  rem create multiline sample file
  >"%_auxFile%" ( for /L %%G in (1,1,3) do echo line %%G is 100%% valid! Sure! Hurrah!)
  rem create one-line sample file
  >"%_auxFile%" echo this line is 100%% valid! Sure! Hurrah!

  echo(
  echo --- file content 
  type "%_auxFile%"

  echo(
  SETLOCAL EnableDelayedExpansion
    echo --- enabled delayed expansion chokes down unescaped exclamation marks^^^! "^!"
    for /F "usebackq delims=" %%G in ("%_auxFile%") do (
      set "_auxLine=%%~G"
      echo loop var=%%~G
      echo _auxLine=!_auxLine!
    )
  ENDLOCAL
  echo(
  SETLOCAL DisableDelayedExpansion
    echo --- toggled delayed expansion works although might be laborious!
    for /F "usebackq delims=" %%G in ("%_auxFile%") do (
      set "_auxLine=%%G"
      echo loop var=%%G
      SETLOCAL EnableDelayedExpansion
        echo _auxLine=!_auxLine!
      ENDLOCAL
    )
  ENDLOCAL
  echo(
  SETLOCAL DisableDelayedExpansion
    echo --- keep delayed expansion DISABLED: use CALL command!
    for /F "usebackq delims=" %%G in ("%_auxFile%") do (
      set "_auxLine=%%G"
      echo loop var=%%G
      call :ProcessVar
    )
  ENDLOCAL

  rem delete the sample file
  del "%_auxFile%"
ENDLOCAL
goto :eof

:ProcessVar
  echo _auxLine=%_auxLine%
  echo WARNING: neither !_auxLine! nor %%G loop variable is available here!  
goto :eof

Note that above script shows proper ways of escaping

  • % percent sign by %% doubling it (delayed expansion does not matter), and
  • ! exclamation mark if delayed expansion is enabled:
    • "^!" if enclosed in a pair of double quotes, then use the cmd and batch-script general escape character ^ caret;
    • ^^^! otherwise, use three ^ carets.

Output:

==> D:\bat\SO\10558316.bat

--- file content
this line is 100% valid! Sure! Hurrah!

--- enabled delayed expansion chokes down unescaped exclamation marks! "!"
loop var=this line is 100% valid Hurrah
_auxLine=this line is 100% valid Hurrah

--- toggled delayed expansion works although might be laborious!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!

--- keep delayed expansion DISABLED: use CALL command!
loop var=this line is 100% valid! Sure! Hurrah!
_auxLine=this line is 100% valid! Sure! Hurrah!
WARNING: !_auxLine! as well as %G loop variables are not available here!

==>