64

Can someone give me an example of where a batch script would act differently with or without delayed expansion? Are there any situations where you would NOT want to use delayed expansion? Thanks.

1

6 Answers 6

84

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Good examples. But even better would be using set /A COUNT=COUNT + 1 or shorter set /A COUNT+=1 to demonstrate that within an arithmetic expression variables can be referenced with just their names. An arithmetic expression is the string after set /A. The help output on running in a command prompt window set /? explains delayed expansion on an IF and a FOR example and also special variable parsing in arithmetic expressions.
It worth clarifying that the expansion happens when a line is parsed, and in the case of blocks delimited by parentheses (e.g. if blocks) the whole block counts as a "line" (source).
12

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.

1 Comment

Your 1Line.txt example seem to separate each value by space. On the other hand, it seems that "49km S of Cantwell, Alaska" should belong together. I have a couple of questions: What is the (intended) specification of your parsing? What is the expected output (can you give a sample output)? For me, it is not clear how the EnableDelayedExpansion makes a difference to solve your use case.
10

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! ==> 

Comments

1

Several answers here answer the "How to use delayed expansion?" question or what happen if you don't use delayed expansion. However, the second question is "Are there any situations where you would NOT want to use delayed expansion?" and a couple answers take this question as "how to avoid the problems caused by using delayed expansion?"

My answer answers the question as I understand it: "In which situations is better to NOT use delayed expansion (instead of use it)?"

If you want to exchange the contents of two variables, the simplest way to perform this is using the %standard% variable expansion:

set "var1=%var2%" & set "var2=%var1%" 

The way that the %standard% expansion works makes possible to achieve this replacemenmt without using any auxiliary variable. As far as I know, the Batch-file "programming language" is the only one that allows to perform this exchange in this way, that is, making good use of a language "feature" (not via a specialized "exchange" instruction/statement).

Comments

0

As pointed in the answer the main usage of the delayed expansion is the setting and accessing variables in brackets context.

Though it can be useful in another situations too.

Parametrizing substring and string substitution:

@echo off setlocal enableDelayedExpansion set "string=test string value" set start=5 set get_next=6 echo #!string:~%start%,%get_next%!# set "search_for=string" set "replace_with=text" echo #!string:%search_for%=%replace_with%!# 

the output will be:

#string# #test text value# 

though this can be achieved with additional call this way is more performant

Using shift command within brackets parameterized argument access

@echo off echo first attempt: ( echo "%~1" shift echo "%~1" ) ::now the shift command will take effect setlocal enableDelayedExpansion echo second attempt: ( set /a argument=1 call echo %%!argument! shift call echo %%!argument! ) 

the output will be:

first attempt: "first argument" "first argument" second attempt: "second argument" "third argument" 

As you can see parameterized argument access can be done only with delayed expansion.

Using for tokens (or function arguments) for parameterization

One more approach for mixing !s and %s this could be useful for nested loops:

@echo off setlocal enabledelayedexpansion set begin=2 set end=2 set string=12345 for /f "tokens=1,2" %%A in ("!begin! !end!") do set "string2=!string:~%%A,%%B!" echo !string2! endlocal 

as you can see now the for command tokens are used as parameters.

Comments

0

Here is one example that kinda blows my mind

set A=)\ echo %A% (echo %A%) 

The first echo works as expected, but the second echo explodes. Those don't have to be standalone parenthesis, they can be a part of an if or for statement, doesn't matter.

 C:\>set A=)\ C:\>echo )\ )\ \) was unexpected at this time. C:\>(echo )\) 

The explanation is that variable expansion occurs before syntax evaluation.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.