Why That Batch For Loop Isn’t Working
Time for another fun foray into Windows batch scripts. Perhaps you've used the FOR /F command to loop through the contents of a file (for instance, perhaps some data that was redirected to a text file from a command). Grab a line, act on its values, and output some text and commands.
Let's set this up. First, we have a data file named SomeAccounts.txt:
Josh
Mary
Suzy
Amanda
Trisha
Ben
Then, we have ProcessAccounts.bat, which we want to just loop through the accounts in the text file, tell us what they are, and tell us the first letter of the account name (just to have something to do):
Except when you do this, you encounter a problem: All of the values from the FOR loop are the same! It's as if the for loop ran the proper number of times, but it just ran on the last record over and over again! See below:
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
What's actually happening is the FOR loop is indeed running over every line, and setting the variables as instructed, but the results of those variables being altered isn't echoed until the FOR loop is complete, so the last value of the variable is what displays. This wouldn't be a problem if you were just using your FOR parameter, in this case %%i, but any variables you set while in the FOR loop, like username, experience this "wait until you're out of the loop" phenomenon.
The fix is simple enough, if you know about it! But I've found the solution to be a bit elusive, which is the whole point of sharing it now.
The key is the setlocal EnableDelayedExpansion command. As explained at ss64.com, making this statement before your FOR loop will enable you to display variables as their value at the moment you're referencing them, or their "intermediate values" while in the middle of the FOR loop. In addition to calling the setlocal command, you then have to reference your variables with the exclamation point (!) rather than percent (%) to indicate that you want to use the intermediate value.
Your script will then look like this:
It will now happily act as desired, outputting these results:
My account, Josh, starts with J.
My account, Mary, starts with M.
My account, Suzy, starts with S.
My account, Amanda, starts with A.
My account, Trisha, starts with T.
My account, Ben, starts with B.

January 7th, 2008 at 11:05 am
Thanks for EnableDelayedExpansion.
It was not practical to call cmd /v each time..
So I used to resort to multiple batch files along the lines of
FOR /F %%i IN (%file%) DO (
set username=%%i
@call processuser.bat %username%
)
EnableDelayedExpansion is much better !!
January 22nd, 2008 at 12:48 pm
Many thanks for this little tip. I’ve been looking for a solution like this for a *really* long time. Very elegant.
January 23rd, 2008 at 2:25 pm
Perfect explanation and example, previously could not figure out where I was going awry. One item to add, I believe in some scenarios you will want to close this with ENDLOCAL. Thanks again.
January 23rd, 2008 at 6:33 pm
Yeah, good point. If there was more to this batch script than just the single loop, that would definitely be a wise idea. Thanks.
August 28th, 2008 at 8:39 am
That solved a problem for me getting the loop to execute properly but then I wanted to put some IF blocks inside the FOR loop ( and/or vice-versa ) and whenever I try that the execution stops at the ELSE
:: @echo off
echo ————————————————–
echo +
echo checking for drive mapping NSPRD. Please wait…
echo +
echo ————————————————–
setlocal EnableDelayedExpansion
set OPT=
set username
set MP=H
set DR=dnr
FOR %%D in (1 2 ) DO (
echo !D!
IF “!D!” == “1″ (
set MP=H
set DR=dnr
set OPT=/user:%USERNAME%
) ELSE (
set MP=Q
set DR=clrc
)
IF EXIST %DR%: (
echo The drive is already mapped.
set MAP=1
) ELSE (
echo The drive was not previously mapped.
set MAP=0
IF
net use %MP%: \\carto-gis\%DR% %OPT%
)
)
PAUSE
REM pause to let the users – or me – see if the mappings worked before closing the command window
exit (0)
When I put the for loop inside an IF Block it won’t run at all.This works when the ‘IF’ and final ‘ )’ are removed or commented but not with them in
:: set tgg=0
:: if “%tgg%” == “0″ (
set OPT=” ”
FOR %%D in (1 2 3 4) DO CALL :L%%D
GOTO :LX
:L1
set MP=H
set DR=dnr
set OPT=/user:%USERNAME%
:: echo 1st
GOTO :LN
:L2
set MP=Q
set DR=datalib
:: echo 2nd
GOTO :LN
:L3
set MP=R
set DR=worklib
:: echo 3rd
GOTO :LN
:L4
set MP=S
set DR=esri
:: echo 4th
:LN
IF EXIST %MP%: (
echo The %MP% drive is already mapped.
set MAP=1
) ELSE (
echo The %MP% drive was not previously mapped.
echo It will be mapped now
set MAP=0
net use %MP%: \\carto-gis\%DR% %OPT%
:: )
:LX
:: PAUSE
Can anyone tell me where I am going wrong, or can’t this be done in a batch – maybe go to vbs
August 29th, 2008 at 9:49 am
Awesome! Thanks!
July 26th, 2009 at 10:27 pm
This post is solid gold! If I’d happened across it first I would have saved 3 days of frustration following other tips that didn’t pan out. The great thing about this one is it’s specific from first to last and doesn’t leave open syntax issues. Perfect for a learner like me. Ironic that Josh is almost apologetic about giving the routine something to do! Thank you, Josh
August 12th, 2009 at 12:01 pm
great stuff ,
I struggled for 2 days before finding this
August 12th, 2009 at 12:27 pm
hmm, not quite what I wanted
I want to increment a count variable within the loop
but that doesn’t work
is there another setlocal variable for this?
August 13th, 2009 at 10:31 am
hmm, not quite what I wanted
I want to increment a count variable within the loop
but that doesn’t work
is there another setlocal variable for this?
found it , variable has to be in ! eg !var!
September 12th, 2009 at 9:26 am
Thanks Joshua ,
I started working on a batch script and was literally going mad with this for last 2 hrs.
Thanks again
April 17th, 2010 at 9:31 am
@jake iam not sure thats really right grtz runescape hack
April 27th, 2010 at 4:04 am
Great tip.
Just ran into this problem. Thanks for the solution.
Some one asked how to increment variable value, I know it’s late, but here it is anyways.
SET VAR1=A, B, C, D
SET count=0
FOR %%j IN (%VAR1%) DO (
SET /A count+=1
ECHO !count! – %%j
)
ECHO %count%
June 28th, 2010 at 2:53 pm
Works, thank you Josh!
July 28th, 2010 at 3:25 am
Brilliant. Worked first time. No fuss. Many thanks.
August 12th, 2010 at 3:14 am
Works flawlessly! You saved my day. Many thanks Josh!
October 1st, 2010 at 7:55 am
[...] has really munged the MSI.There is one exception to the above, gratefully found reported here:http://blog.crankybit.com/why-that-batch-for-loop-isnt-working/It occurs when there is a separate variable to be set inside the FOR loop. In these cases, one [...]
November 4th, 2010 at 4:57 am
Wow! That was exactly what I was looking for. This was driving me mad. Excellent work.
January 27th, 2011 at 6:40 am
I’ve been struggling with this problem what seems like forever. I cannot tell you how greatful I am! A million thanks.
April 27th, 2011 at 9:50 am
I was banging my head in the wall until I found this article.
Thank you very much!
July 3rd, 2011 at 7:33 am
Thank you very much! This is very helpful!
July 28th, 2011 at 1:02 pm
Wow! I was banging myself until I found your solution. Thanks Dude!!
August 4th, 2011 at 8:51 am
This is really useful. I was searching for this exactly. Many thanks !!
October 2nd, 2011 at 3:05 pm
Hi All,
ok so just curious…
This does not work:
:: Hi There
@echo off
for /L %%a IN (1,1,10) do (
echo hi there
:: COMMENT
)
exit /b
But this DOES:
:: Hi There
@echo off
for /L %%a IN (1,1,10) do (
echo hi there
REM COMMENT
)
exit /b
POR QWA???
Cheerz,
Neal
October 26th, 2011 at 3:23 pm
Thanks! It helped me to find out that the variable that depends on the parameter of the for loop has to be written within between two ‘!’
February 17th, 2012 at 4:02 pm
I am new at window’s scripting and have a question.
I have a file called temp_file that has the following 3 lines
A
B
C
FOR /f %%i in (%temp_file%) DO echo %%i
It correctly shows
A
B
C
But when I try executing
FOR /f %%i in (‘type %temp_file%’) DO (@call cycle.bat %%i)
it only executes cycle.bat once and passes A as a parameter, instead of looping 3 times.
Any help is appreciated.
February 22nd, 2012 at 10:14 am
thank you!
February 27th, 2012 at 2:25 pm
Dianna,
I have attempted to recreate your problem but it has worked every time for me. What I would like to alert you of, is that you needn’t use ‘type’ . You can just use (filename.txt) and it will do the same thing. This may help in your situation.
March 14th, 2012 at 1:09 am
This post is very helpful!
March 18th, 2012 at 7:50 pm
This post was very helpful…
i want to learn batch scripting so i volunteered to create a simple monitoring script here…
i was stuck at this problem for three days..
i did use setLocal EnableDelayedExpansion, but still cannot echo the variable that i want to display..
so the key was using “!” instead of “%” while inside the FOR loop.
Thanks again!
May 13th, 2012 at 11:29 pm
Thanks mate, your article was a lifesaver. This problem was doing my head in!
May 15th, 2012 at 8:56 am
Thank you for this wonderful tip. For loop incremeters in Batch Script used to be the toughest challenge for me and I used to use a lot of GOTO labels in my scripts earlier. This solves my problem like a Charm!
Thanks once again!
June 7th, 2012 at 8:39 am
As might have been mentioned before – this is fantastic info! Thanks
August 23rd, 2012 at 11:29 am
Perfect. I have struggled with this problem for ages and at last you have solved it for me.
October 10th, 2012 at 12:49 pm
Thanks. I knew this was the problem, but I was struggling with solving it.
February 6th, 2013 at 10:40 am
Hi,
I have the following code to check if a file exists in a folder. If the file is there is should exit, if it doesn’t is should run continueously. It exit every time after one run. Any ideas?
for /f %%a IN (c:\jason\test.txt) do (Echo “File exists”
exit
)
February 7th, 2013 at 3:33 pm
Jason – That’s not quite right. Here’s how you check the existence of a file:
Stack Overflow: How to check if a file exists from inside a batch file
Hope this helps.
February 13th, 2013 at 10:42 pm
@Neal Rosen: The reason the ‘::’ comments don’t work is that is really just a cheat. The only way to comment out a line is using the ‘REM’.
However, if any line starts with a ‘:’, then it is assumed to be a label and not actually processed. However, since labels aren’t allowed inside loops, you get the error when used there.
April 21st, 2013 at 10:17 pm
I am looking to copy first 5 files (or 5 files modified by date)in the directory to another directory to do some encoding. I am struck with /F and /L options. Can you please help?
April 29th, 2013 at 10:07 am
Hello!
I just want to do a simple counter within a for loop.
This below does not seem to work. The variable count never gets incremented. The setlocal enabledelayedexpansion does not help either. Can someone help me?
Thanks in advance!
setlocal enabledelayedexpansion
set count=0
for /l %%i in (0,1,10) do (
::set /a count+=1
echo %count% %%i
)
endlocal