Windows batch: using goto in a loop - batch-file

The following code runs a continuous loop, which increments the variable counter. When counter reaches 10, it should call the set_varibles routine, and then continue in the loop from the place set_variables was called. However, the goto does not work. What is wrong, and is there a better solution? Thanks
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
:set_variables
SET t=ooo
:my_loop
set /a counter=0
for /L %%n in (1,0,10) do (
echo !counter!
set /a counter=counter+1
if !counter! equ 10 (
echo now TEN
goto set_variables
)
)
#echo on
ENDLOCAL

CALL a subroutine (:label)
The following code runs a continuous loop, which increments the
variable counter. When counter reaches 10, it should call the
set_varibles routine, and then continue in the loop from the place
set_variables was called.
Exactly as proposed: echo now TEN and call :set_variables every tenth pass the endless loop.
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
call :set_variables
:my_loop
set /a counter=0
for /L %%n in (1,0,10) do (
echo !counter!
set /a counter=counter+1
set /a auxcounter=counter%%10
if !auxcounter! equ 0 (
echo now TEN
call :set_variables
)
)
#echo on
ENDLOCAL
#goto :eof
:set_variables
SET t=ooo
#goto :eof

Your main problem is not the goto command, but the for /l
While usually a goto command will cancel any block of code (code in parenthesis), for /l loops are a different beast. There are only two ways to leave a running for /l: an exit command (not exit /b unless the code is running in a command line context), or if the counter is greater than the end value.
Than means that the goto can not go out of the loop. It will cancel the execution of the code inside the do clause, but the loop will not be finished until the end value is reached, and your for loop
start step end
for /L %%n in ( 1, 0, 10 ) do ...
has a step value of 0 making it impossible to reach the end value.
The equivalent to your posted code could be
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
:set_variables
SET t=ooo
:my_loop
for /L %%n in (0,1,9) do (
echo %%n
set /a counter=%%n+1
rem ....
)
echo now TEN
goto set_variables

Isn't this what you want:
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
:set_variables
:my_loop
for /L %%n in (1,1,10) do (
echo %%n
if %%n equ 10 (
echo now TEN
SET t=ooo
)
)
goto my_loop
#echo on
ENDLOCAL

Using GOTO within parenthesis - including FOR and IF commands - will break their context.
Some things look odd in your script. Why have a continuous loop and then restart it when a counter reaches 10? Why not use "in (1,1,10)" like in Monacraft's answer?
Why does this tweaked version not do what you want?
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set /a outer_counter=0
:set_variables
echo start of set_variables
SET t=ooo
:my_loop
set /a counter=0
for /L %%n in (1,1,10) do (
echo !counter!
set /a counter=counter+1
set /a outer_counter=outer_counter+1
if !counter! equ 10 (
echo now TEN
)
)
if !outer_counter! geq 100 goto after_loop
goto set_variables
:after_loop
echo FINISHED
#echo on
ENDLOCAL
pause

I hate when additional specifications are placed in comments instead of the question description, but this comment appeared below Monacraft's answer:
I want to re-run the routine which is called periodically within the loop.
Everytime the counter reaches a certain value, the routine must be called
and the counter variable reset.
So I think that the solution to all OP's requests is this:
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
call :set_variables
:my_loop
for /L %%n in (1,0,10) do (
echo !counter!
set /a counter=counter+1
if !counter! equ 10 (
echo now TEN
call :set_variables
)
)
No matter what is placed here, because a *continuous loop* NEVER ENDS!
#echo on
ENDLOCAL
:set_variables
SET t=ooo
set /a counter=0
exit /B

Related

Windows Batch:Nested for loop counter not working

I'm a novice at batch and been trying to get this to work properly but can't figure out what I'm doing wrong. The counter doesn't increment for some reason?
#echo off
set local enabledelayedexpansion
set /a counter=0
for /F "delims=" %%a in ('dir /b/ad/o "C:\Sources"') do (
for /F "delims=" %%i in ('dir /b/ad/o "C:\Sources\%%a"') do (
set a[%counter]=%%i
set /a counter=counter+1
echo value of counter is: %counter%
)
)
echo array 0 is: %a[0]%
echo array 1 is: %a[1]%
The SET LOCAL should really be SETLOCAL. It's a single command. Also the nested variables should be refernced with !var! and not %var%. If you use %var% it will use the outer scope (and not work correctly).
https://ss64.com/nt/delayedexpansion.html
#ECHO OFF
REM "SETLOCAL" not "SET LOCAL"
SETLOCAL enabledelayedexpansion
SET counter=0
FOR /L %%a IN (1,1,10) DO (
FOR /L %%i IN (1,1,10) DO (
REM USE "!" instead of "%"
SET X[!counter!]=%%i
SET /a counter=!counter! +1
echo value of counter is: !counter!
)
)
ECHO array 0 is: %X[0]%
ECHO array 1 is: %X[1]%
BTW, if you want to have your variables just scoped to your batchfile you should end your script with ENDLOCAL

need help getting batch file to read input but it works fine on the first part strangely

so heres my code the first part executes perfectly but the second doesnt it just displays each variable as blank. im not sure why as its formatted the same also would it be possible to put the :readprofiles part in a working for variable?:
purpose of the program: to list out a directory as profiles along with numbered choices to select. basically a menu.
#echo off
setlocal enabledelayedexpansion
set Counter=1
for /f "DELIMS=" %%i in (test.txt) do (
set "Line_!Counter!=%%i"
set /a Counter+=1
)
set /a NumLines=Counter - 1
:: this part is a test
echo %Line_1%
echo %Line_2%
echo %Line_3%
echo %Line_4%
echo %Line_5%
echo %Line_6%
:: end test
set Counter=1
:readprofiles
if %Counter%==%NumLines% goto pause
echo %Counter%. %Line_!Counter!%
set /a Counter+=1
goto readprofiles
:pause
pause
#echo off
setlocal enabledelayedexpansion
set Counter=0
for /f "DELIMS=" %%i in (test.txt) do (
set /a Counter+=1
set "Line_!Counter!=%%i"
)
For /L %%C in (1,1,%Counter%) Do echo %%C. !Line_%%C!
Pause

Batch: Everything after for loop does not occur

Everything before the for loop works fine, i can add "pause" to it and it pauses, but inside the for loop it does not work. I have tried to add pause but it doesn't, and the pause after the loop does not occur.
#echo off
setlocal enabledelayedexpansion
setlocal
for %%g in ( * ) do (
pause
echo %%g
set strEnd=___EndOfString___
set strFilename=%%g
set strFile=!strFilename!!strEnd!
echo !strFile!
pause
:loop
set strChar=!strFile:~0,1!
set strFile=!strFile:~1!
if !strChar!==a echo A
pause
if not !strFile!==!strEnd! goto loop
)
pause
endlocal
This construct should work. You can adjust timeouts duration.
#echo off
setlocal enabledelayedexpansion
for %%g in ( * ) do (echo %%g
set strEnd=___EndOfString___
set strFilename=%%g
set strFile=!strFilename!!strEnd!
echo !strFile!
timeout 2 >nul
call :loop
)
pause
exit /b
:loop
:: add more code here
timeout 2 >nul
exit /b
However, your original script inside the :loop is not correct, and needs to be changed depending on the batch purpose.

How do I make something loop 10 times then continue in batch?

In batch, I want to run something 10 times and then goto continue.
How would I do this?
Here's one approach:
#echo off
setlocal enabledelayedexpansion
echo before
set i=0
:loop1
echo something 10 times
set /a i+=1
if !i!==10 goto continue
goto loop1
:continue
echo after
exit /b 0
The easiest way to do this is a for /L loop.
for /L %%A in (1,1,10) do (
rem your code here
)
goto continue
The format for a for /L loop is (start,step,end), so (1,1,10) means start at 1 and count to 10 by 1s.

How to set a variable inside a loop for /F

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?
There are two methods to setting and using variables within for loops and parentheses scope.
setlocal enabledelayedexpansion see setlocal /? 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.
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.
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
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:
Difference between %variable% and !variable! in batch file
Variable usage in 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.
)
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%
Try this:
setlocal EnableDelayedExpansion
...
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
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
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
)
The following should work:
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)

Resources