How do SETLOCAL and ENABLEDELAYEDEXPANSION work?

122,189

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 outstanding SETLOCAL 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.

Share:
122,189
Anthony Miller
Author by

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, 2022

Comments

  • Anthony Miller
    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
    Anthony Miller over 11 years
    what about the 2nd part to the question?
  • Alex K.
    Alex K. over 11 years
    Well it would only have an adverse effect if you enabled it, then wrote some code that behaved differently were it not on
  • jeb
    jeb over 11 years
    With EnableDelayedExpansion you got even problems if your text contains only a single ! like echo Hello! and with a exclamation mark in a line you got also problems with carets echo "Caret^" is gone!
  • Anthony Miller
    Anthony Miller almost 9 years
    Over 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
    yoyo over 7 years
    And 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
    Kyle Delaney over 5 years
    To be clear, delayed expansion does nothing if no exclamation marks are present in your commands, right?
  • Michel de Ruiter
    Michel de Ruiter over 5 years
    Right! Exclamation marks do have other, subtle effects.
  • Steve Hollasch
    Steve Hollasch over 4 years
    @Mechaflash Good points, but the problem that @Rob is talking about is the hard coupling between setlocal and enabledelayedexpansion. If you want a script that affects the environment, but also wants enabledelayedexpansion, 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 the setlocal 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
    WesternGun over 3 years
    I 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.