How to set a variable inside a loop for /F
Batch FileCmdBatch File Problem Overview
I made this code
dir /B /S %RepToRead% > %FileName%
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
echo %%a
is working fine but echo %z%
returns "echo disabled".
I need to set a %z% because I want to split the variable like %z:~7%
Any ideas?
Batch File Solutions
Solution 1 - Batch File
There are two methods to setting and using variables within for loops and parentheses scope.
-
setlocal enabledelayedexpansion
seesetlocal /?
for help. This only works on XP/2000 or newer versions of Windows. then use!variable!
instead of%variable%
inside the loop... -
Create a batch function using batch goto labels
:Label
.Example:
for /F "tokens=*" %%a in ('type %FileName%') do call :Foo %%a goto End :Foo set z=%1 echo %z% echo %1 goto :eof :End
Batch functions are very useful mechanism.
Solution 2 - Batch File
You probably want SETLOCAL ENABLEDELAYEDEXPANSION
. See https://devblogs.microsoft.com/oldnewthing/20060823-00/?p=29993 for details.
Basically: Normal %variables%
are expanded right aftercmd.exe
reads the command. In your case the "command" is the whole
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
loop. At that point z
has no value yet, so echo %z%
turns into echo
. Then the loop is executed and z
is set, but its value isn't used anymore.
SETLOCAL ENABLEDELAYEDEXPANSION
enables an additional syntax, !variable!
. This also expands variables but it only does so right before each (sub-)command is executed.
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
This gives you the current value of z
each time the echo
runs.
Solution 3 - Batch File
I struggeld for many hours on this. This is my loop to register command line vars. Example : Register.bat /param1:value1 /param2:value2
What is does, is loop all the commandline params, and that set the variable with the proper name to the value.
After that, you can just use set value=!param1! set value2=!param2!
regardless the sequence the params are given. (so called named parameters). Note the !<>!, instead of the %<>%.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%P IN (%*) DO (
call :processParam %%P
)
goto:End
:processParam [%1 - param]
@echo "processparam : %1"
FOR /F "tokens=1,2 delims=:" %%G IN ("%1") DO (
@echo a,b %%G %%H
set nameWithSlash=%%G
set name=!nameWithSlash:~1!
@echo n=!name!
set value=%%H
set !name!=!value!
)
goto :eof
:End
Solution 4 - Batch File
Simple example of batch code using %var%, !var!, and %%.
In this example code, focus here is that we want to capture a start time using the built in variable TIME (using time because it always changes automatically):
Code:
@echo off
setlocal enabledelayedexpansion
SET "SERVICES_LIST=MMS ARSM MMS2"
SET START=%TIME%
SET "LAST_SERVICE="
for %%A in (%SERVICES_LIST%) do (
SET START=!TIME!
CALL :SOME_FUNCTION %%A
SET "LAST_SERVICE=%%A"
ping -n 5 127.0.0.1 > NUL
SET OTHER=!START!
if !OTHER! EQU !START! (
echo !OTHER! is equal to !START! as expected
) ELSE (
echo NOTHING
)
)
ECHO Last service run was %LAST_SERVICE%
:: Function declared like this
:SOME_FUNCTION
echo Running: %1
EXIT /B 0
Comments on code:
- Use enabledelayedexpansion
- The first three SET lines are typical uses of the SET command, use this most of the time.
- The next line is a for loop, must use %%A for iteration, then %%B if a loop inside it etc.. You can not use long variable names.
- To access a changed variable such as the time variable, you must use !! or set with !! (have enableddelayexpansion enabled).
- When looping in for loop each iteration is accessed as the %%A variable.
- The code in the for loop is point out the various ways to set a variable. Looking at 'SET OTHER=!START!', if you were to change to SET OTHER=%START% you will see why !! is needed. (hint: you will see NOTHING) output.
- In short !! is more likely needed inside of loops, %var% in general, %% always a for loop.
Further reading
Use the following links to determine why in more detail:
Solution 5 - Batch File
To expand on the answer I came here to get a better understanding so I wrote this that can explain it and helped me too.
It has the setlocal DisableDelayedExpansion
in there so you can locally set this as you wish between the setlocal EnableDelayedExpansion
and it.
@echo off
title %~nx0
for /f "tokens=*" %%A in ("Some Thing") do (
setlocal EnableDelayedExpansion
set z=%%A
echo !z! Echoing the assigned variable in setlocal scope.
echo %%A Echoing the variable in local scope.
setlocal DisableDelayedExpansion
echo !z! &rem !z! Neither of these now work, which makes sense.
echo %z% &rem ECHO is off. Neither of these now work, which makes sense.
echo %%A Echoing the variable in its local scope, will always work.
)
Solution 6 - Batch File
set list = a1-2019 a3-2018 a4-2017
setlocal enabledelayedexpansion
set backup=
set bb1=
for /d %%d in (%list%) do (
set td=%%d
set x=!td!
set y=!td!
set y=!y:~-4!
if !y! gtr !bb1! (
set bb1=!y!
set backup=!x!
)
)
rem: backup will be 2019
echo %backup%
Solution 7 - Batch File
You can use a macro if you access a variable outside the scope
@echo off
::Define macro
set "sset=set"
for /l %%a in (1,1,4) do (
::set in loop
%sset% /a "x[%%a]=%%a*%%a"
if %%a equ 4 (
:: set in condition
%sset% "x[%%a]=x Condition"
%sset% "y=y Condition"
)
)
echo x1=%x[1]% x2=%x[2]% x3=%x[3]% x4=%x[4]% y=%y%
:: Bonus. enableDelayedExpansion used to access massive from the loop
setlocal enableDelayedExpansion
echo Echo from the loop
for /l %%a in (1,1,4) do (
::echo in one line - echo|set /p =
echo|set /p "=x%%a=!x[%%a]! "
if %%a equ 4 echo y=%y%
)
pause
Solution 8 - Batch File
Try this:
setlocal EnableDelayedExpansion
...
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
Solution 9 - Batch File
I know this isn't what's asked but I benefited from this method, when trying to set a variable within a "loop". Uses an array. Alternative implementation option.
SETLOCAL ENABLEDELAYEDEXPANSION
...
set Services[0]=SERVICE1
set Services[1]=SERVICE2
set Services[2]=SERVICE3
set "i=0"
:ServicesLoop
if defined Services[%i%] (
set SERVICE=!Services[%i%]!
echo CurrentService: !SERVICE!
set /a "i+=1"
GOTO :ServicesLoop
)
Solution 10 - Batch File
The following should work:
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)