How do SETLOCAL and ENABLEDELAYEDEXPANSION work?
Solution 1
ENABLEDELAYEDEXPANSION
is a parameter passed to the SETLOCAL
command (look at setlocal /?
)
Its effect lives for the duration of the script, or an ENDLOCAL
:
When the end of a batch script is reached, an implied
ENDLOCAL
is executed for any outstandingSETLOCAL
commands issued by that batch script.
In particular, this means that if you use SETLOCAL ENABLEDELAYEDEXPANSION
in a script, any environment variable changes are lost at the end of it unless you take special measures.
Solution 2
I think you should understand what delayed expansion is. The existing answers don't explain it (sufficiently) IMHO.
Typing SET /?
explains the thing reasonably well:
Delayed environment variable expansion is useful for getting around the limitations of the current expansion which happens when a line of text is read, not when it is executed. The following example demonstrates the problem with immediate variable expansion:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
would never display the message, since the %VAR% in BOTH IF statements is substituted when the first IF statement is read, since it logically includes the body of the IF, which is a compound statement. So the IF inside the compound statement is really comparing "before" with "after" which will never be equal. Similarly, the following example will not work as expected:
set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
in that it will NOT build up a list of files in the current directory, but instead will just set the LIST variable to the last file found. Again, this is because the %LIST% is expanded just once when the FOR statement is read, and at that time the LIST variable is empty. So the actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different character (the exclamation mark) to expand environment variables at execution time. If delayed variable expansion is enabled, the above examples could be written as follows to work as intended:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "!VAR!" == "after" @echo If you see this, it worked ) set LIST= for %i in (*) do set LIST=!LIST! %i echo %LIST%
Another example is this batch file:
@echo off
setlocal enabledelayedexpansion
set b=z1
for %%a in (x1 y1) do (
set b=%%a
echo !b:1=2!
)
This prints x2
and y2
: every 1 gets replaced by a 2.
Without setlocal enabledelayedexpansion
, exclamation marks are just that, so it will echo !b:1=2!
twice.
Because normal environment variables are expanded when a (block) statement is read, expanding %b:1=2%
uses the value b
has before the loop: z2
(but y2
when not set).
Solution 3
The ENABLEDELAYEDEXPANSION part is REQUIRED in certain programs that use delayed expansion, that is, that takes the value of variables that were modified inside IF or FOR commands by enclosing their names in exclamation-marks.
If you enable this expansion in a script that does not require it, the script behaves different only if it contains names enclosed in exclamation-marks !LIKE! !THESE!. Usually the name is just erased, but if a variable with the same name exist by chance, then the result is unpredictable and depends on the value of such variable and the place where it appears.
The SETLOCAL part is REQUIRED in just a few specialized (recursive) programs, but is commonly used when you want to be sure to not modify any existent variable with the same name by chance or if you want to automatically delete all the variables used in your program. However, because there is not a separate command to enable the delayed expansion, programs that require this must also include the SETLOCAL part.

Anthony Miller
"Your competition is a hungry immigrant with a digital handheld assistant. America is made up of immigrants... if your children are to be successful they must act like immigrants in their own country. Just by being born here doesn't give you the ability to be successful... it is the work ethic... the pioneering ethic... the service ethic that will win. Your competition is always a hungry immigrant with a digital assistant: hungry in the belly for food, hungry in the mind for knowledge, and the hunger is something that should never leave you." ~Dr. Dennis Waitley
Updated on July 05, 2022Comments
-
Anthony Miller 6 months
I notice in most scripts, the two are usually in the same line as so:
SETLOCAL ENABLEDELAYEDEXPANSION
Are the two in fact separate commands and can be written on separate lines?
Will setting
ENABLEDELAYEDEXPANSION
have an adverse effect on a script if it is set on the first lines of the script and not disabled until the end of the script? -
Anthony Miller over 11 yearswhat about the 2nd part to the question?
-
Alex K. over 11 yearsWell it would only have an adverse effect if you enabled it, then wrote some code that behaved differently were it not on
-
jeb over 11 yearsWith EnableDelayedExpansion you got even problems if your text contains only a single
!
likeecho Hello!
and with a exclamation mark in a line you got also problems with caretsecho "Caret^" is gone!
-
Anthony Miller almost 9 yearsOver the years of using batch files, I've automatically added that directive to each and every one of em and never turn it off. I wouldn't go as far as to enable it by default for a system because you never know if a system-ran batch script or some other company's batch scripts may have conflicts with it. This IS Windows we're talking about ;)
-
yoyo over 7 yearsAnd it seems that unfortunately there is no way to enable delayed expansion without invoking setlocal, even though these control what ought to be separate features. :-P
-
Kyle Delaney over 5 yearsTo be clear, delayed expansion does nothing if no exclamation marks are present in your commands, right?
-
Michel de Ruiter over 5 yearsRight! Exclamation marks do have other, subtle effects.
-
Steve Hollasch over 4 years@Mechaflash Good points, but the problem that @Rob is talking about is the hard coupling between
setlocal
andenabledelayedexpansion
. If you want a script that affects the environment, but also wantsenabledelayedexpansion
, then you need to resort to hacks (like temporary script files) to make it work. -
LeoVannini almost 4 years+1 Encountered this issue when checking
%errorlevel%
returned from another batch file in a for loop. Added thesetlocal enabledelayedexpansion
before the for loop, and changed the check from%errorlevel%
to!errorlevel!
and it worked like a charm. Thanks again for the in depth explanation! -
WesternGun over 3 yearsI think Alex K has answer that part:
ENDLOCAL
is omitted because when we reach the end,SETLOCAL
stops to function. We programmer just loves to keep it short... but is less verbose.