Delayed Expansion

Maintained on

You update a variable inside a FOR loop or IF block in a batch file, but echo still prints the old value. This is one of the most common problems in batch scripting.

The fix is delayed environment variable expansion.

In short: add setlocal enabledelayedexpansion and replace %variable% with !variable! inside the block. This article explains the expansion timing behind the problem and shows how to use delayed expansion with sample code.

The Problem: Variables Do Not Update Inside Blocks

Look at the following batch file.

@echo off
setlocal

set COUNT=0
for %%f in (*.txt) do (
    set /a COUNT+=1
    echo File %COUNT%: %%f
)
echo Total: %COUNT%

endlocal

This script tries to count text files, but the result is not what you expect.

- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>problem.cmd
File 0: file1.txt
File 0: file2.txt
File 0: file3.txt
Total: 0
C:\users\user>

COUNT is always displayed as 0.

The Cause: When Variables Are Expanded

In batch files, a block enclosed in parentheses () is parsed as a whole before it runs. During parsing, %variable% is replaced with the value it had before the block started.

In the example above, %COUNT% is replaced with 0 (its value before the for loop), so updating it inside the loop has no visible effect.

rem When the whole block is parsed, %COUNT% is replaced with 0
for %%f in (*.txt) do (
    set /a COUNT+=1
    echo File 0: %%f    <- %COUNT% has already become 0
)

The Solution: Enable Delayed Expansion

Enable delayed expansion and use !variable! instead of %variable%. The variable is then expanded at execution time.

Steps

  1. Write setlocal enabledelayedexpansion
  2. Change variable references inside the block to !variable!
@echo off
setlocal enabledelayedexpansion

set COUNT=0
for %%f in (*.txt) do (
    set /a COUNT+=1
    echo File !COUNT!: %%f
)
echo Total: !COUNT!

endlocal
- □ ×
Command Prompt Icon
Command Prompt
Microsoft Windows [Version xx.x.xxxxx.xxx]
(c) 2026 Ribbit App Development All rights reserved.
 
C:\users\user>solution.cmd
File 1: file1.txt
File 2: file2.txt
File 3: file3.txt
Total: 3
C:\users\user>
おすすめ書籍※ 広告を含む場合があります
中小企業経営者のためのRPA入門 RPA導入を成功させる方法

中小企業経営者のためのRPA入門 RPA導入を成功させる方法

60分でわかる! AIエージェント 超入門

60分でわかる! AIエージェント 超入門

コマンドラインの黒い画面が怖いんです。

コマンドラインの黒い画面が怖いんです。

知識・才能ゼロでもらく~に月10万円稼ぐ! よくわかるAI副業超入門

知識・才能ゼロでもらく~に月10万円稼ぐ! よくわかるAI副業超入門

Now COUNT is incremented correctly.

%variable% vs. !variable!

SyntaxExpandedUse for
%variable%At parse timeOrdinary references outside blocks
!variable!At execution timeVariables that change inside a block
Scope of delayed expansion

setlocal enabledelayedexpansion stays in effect until the matching endlocal (or until the batch file ends if there is none). Variables changed inside setlocal are also restored by endlocal. To enable it for the whole script, put it at the top of the file.

The Same Problem in IF Blocks

The same problem occurs in IF blocks, not just FOR loops.

@echo off
setlocal

set RESULT=NG

if exist "data.txt" (
    set RESULT=OK
    echo Result: %RESULT%
)

Even when data.txt exists, this prints Result: NG. Fix it with delayed expansion.

@echo off
setlocal enabledelayedexpansion

set RESULT=NG

if exist "data.txt" (
    set RESULT=OK
    echo Result: !RESULT!
)

Typical Cases That Need Delayed Expansion

Numbering file names

@echo off
setlocal enabledelayedexpansion

set NUM=0
for /f "delims=" %%f in ('dir /b /a-d *.jpg') do (
    set /a NUM+=1
    set PADDED=00!NUM!
    set PADDED=!PADDED:~-3!
    ren "%%f" "photo_!PADDED!.jpg"
)

echo Renaming finished.
endlocal
Looping while renaming

If you rename files inside for %%f in (*.jpg), renamed files may be picked up by the loop again. Reading the output of dir /b with for /f loops over the list as it was before the loop started, which is safer.

Accumulating values in a variable

@echo off
setlocal enabledelayedexpansion

set ALL_FILES=
for %%f in (*.txt) do (
    set ALL_FILES=!ALL_FILES! %%f
)

echo Files found:!ALL_FILES!
endlocal

Things to Watch Out For

Strings that contain an exclamation mark !

When delayed expansion is enabled, ! in a string is treated as a variable delimiter. Be careful with file names and paths that contain !.

rem With delayed expansion on, the "!" is lost and this prints C:\ImportantFiles
set PATH_WITH_BANG=C:\Important!Files
echo !PATH_WITH_BANG!
Strings containing exclamation marks

With delayed expansion enabled, strings containing ! may be expanded unexpectedly. If you need to handle such paths, disable delayed expansion for that part with setlocal disabledelayedexpansion, or escape the character as ^^!.

Use it only where needed

Delayed expansion is powerful, but you do not need it everywhere. Use ordinary %variable% for simple references outside blocks.

Summary

ItemDetails
How to enablesetlocal enabledelayedexpansion
Delayed reference syntax!variable_name!
Where the problem occursInside FOR loops and IF blocks
CautionStrings containing ! can be misinterpreted

Delayed expansion is essential knowledge for handling variables correctly inside loops and blocks in batch files.

Quiz

Practice Problem

Which statement correctly describes when %variable% and !variable! are expanded?

回答がサーバーに送信されることはありません
#Command Prompt #Batch File #Delayed Expansion #Variables #enabledelayedexpansion