GOTO inside FOR loop ? Can it be done ?

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
shodan
Posts: 54
Joined: 01 May 2023 01:49

GOTO inside FOR loop ? Can it be done ?

#1 Post by shodan » 17 Sep 2023 16:45

Hi,

I'm trying to skip a bit of code using a goto inside a for loop

What I want to do is skip to the end of the for loop and do the next iteration

I've made a small test function to try this out

Code: Select all

:setup
:main
Call :GotoInFor-DEMO
GoTo :EOF

:GotoInFor-DEMO

echo.&echo looping through for testing
for /l %%a in (1,1,10) do (
	echo start of the loop %%a
	echo middle of the loop %%a
	echo end of the loop %%a
	)

echo.&echo looping through with a goto
for /l %%a in (1,1,10) do (
	echo start of the loop %%a
	GoTo :UniqueLabelGoToInFor
	echo middle of the loop %%a
	:UniqueLabelGoToInFor
	echo end of the loop %%a
	)

echo.&echo looping through with a if goto
for /l %%a in (1,1,10) do (
	echo start of the loop %%a
	if %%a LSS 5 GoTo :UniqueLabelGoToInFor
	echo middle of the loop %%a
	:UniqueLabelGoToInFor
	echo end of the loop %%a
	)

GoTo :EOF
The output of this code is

Code: Select all

MoveObject-DEMO.bat

looping through for testing
start of the loop 1
middle of the loop 1
end of the loop 1
start of the loop 2
middle of the loop 2
end of the loop 2
start of the loop 3
middle of the loop 3
end of the loop 3
start of the loop 4
middle of the loop 4
end of the loop 4
start of the loop 5
middle of the loop 5
end of the loop 5
start of the loop 6
middle of the loop 6
end of the loop 6
start of the loop 7
middle of the loop 7
end of the loop 7
start of the loop 8
middle of the loop 8
end of the loop 8
start of the loop 9
middle of the loop 9
end of the loop 9
start of the loop 10
middle of the loop 10
end of the loop 10

looping through with a goto
start of the loop 1
end of the loop %a

So what is happening is that it quits the for loop and skips to the labels and starts executing from there.

Even if that's code inside the loop, but it's not looping anymore


So using GOTO in a for loop, only lets you exit the loop ?


For my usage
I guess I could use a IF statement, but I get weird bugs with variable expansion and I don't really know why, is there another way to skip to the next iteration ?

shodan
Posts: 54
Joined: 01 May 2023 01:49

Re: GOTO inside FOR loop ? Can it be done ?

#2 Post by shodan » 17 Sep 2023 17:07

Here is my actual code I was trying to make work

This version tries to skip the code entirely when the IF evaluates to true
It fails because the goto exits the loop entirely

Code: Select all

:MoveObjectOnly
set "_MoveObjectOnly_prefix=_MOI"
set _MOI_Input=%~1
set _MOI_Output=%~2

REM this should copy all variable suffix to the new destination
for /f "tokens=1,2* delims==" %%a in ('set %_MOI_Input% 2^>nul') do  (
	if "[%_MOI_Input%]" NEQ "[%%a]" GoTo :MoveObjectOnly-skip
	setlocal enabledelayedexpansion
	set _MOI_Suffix_buffer_input=%%a
	set _MOI_Suffix_buffer_output=%_MOI_Output%!_MOI_Suffix_buffer_input:%_MOI_Input%=!
	for /f "tokens=*" %%Z in ('echo.!_MOI_Suffix_buffer_output!') do (
																endlocal 
																echo set %%Z=%%b
																if "[%_MOI_Input%]" NEQ "[%%a]" set %%Z=%%b
																)
	if defined _MOI_localscope endlocal
	:MoveObjectOnly-skip
	)
if defined _MOI_localscope endlocal

REM this clears the old base variable
if "[%_MOI_Input%]" NEQ "[]" for /f "tokens=1,2 delims==" %%a in ('set %_MOI_Input% 2^>nul') do if "[%_MOI_Input%]" NEQ "[%%a]" set %%a=
Call :ClearVariablesByPrefix %_MoveObjectOnly_prefix% _MoveObjectOnly 
if "[%~3]" NEQ "[]" if "[%~4]" NEQ "[]" shift & shift & GoTo :MoveObjectOnly
GoTo :EOF
This version put the nested for loop on the IF
This version the "set" command runs correctly but the results are lost, the variables don't actually change

Code: Select all

:MoveObjectOnly
set "_MoveObjectOnly_prefix=_MOI"
set _MOI_Input=%~1
set _MOI_Output=%~2

REM this should copy all variable suffix to the new destination
for /f "tokens=1,2* delims==" %%a in ('set %_MOI_Input% 2^>nul') do  (
	setlocal enabledelayedexpansion
	set _MOI_Suffix_buffer_input=%%a
	set _MOI_Suffix_buffer_output=%_MOI_Output%!_MOI_Suffix_buffer_input:%_MOI_Input%=!
	if "[%_MOI_Input%]" NEQ "[%%a]" for /f "tokens=*" %%Z in ('echo.!_MOI_Suffix_buffer_output!') do (
																endlocal 
																echo set %%Z=%%b
																set %%Z=%%b
																)
	if defined _MOI_localscope endlocal
	)
if defined _MOI_localscope endlocal

REM this clears the old base variable
if "[%_MOI_Input%]" NEQ "[]" for /f "tokens=1,2 delims==" %%a in ('set %_MOI_Input% 2^>nul') do if "[%_MOI_Input%]" NEQ "[%%a]" set %%a=
Call :ClearVariablesByPrefix %_MoveObjectOnly_prefix% _MoveObjectOnly 
if "[%~3]" NEQ "[]" if "[%~4]" NEQ "[]" shift & shift & GoTo :MoveObjectOnly
GoTo :EOF
This version works as expected

Code: Select all

:MoveObjectOnly
set "_MoveObjectOnly_prefix=_MOI"
set _MOI_Input=%~1
set _MOI_Output=%~2

REM this should copy all variable suffix to the new destination
for /f "tokens=1,2* delims==" %%a in ('set %_MOI_Input% 2^>nul') do  (
	setlocal enabledelayedexpansion
	set _MOI_Suffix_buffer_input=%%a
	set _MOI_Suffix_buffer_output=%_MOI_Output%!_MOI_Suffix_buffer_input:%_MOI_Input%=!
	for /f "tokens=*" %%Z in ('echo.!_MOI_Suffix_buffer_output!') do (
																endlocal 
																echo set %%Z=%%b
																if "[%_MOI_Input%]" NEQ "[%%a]" set %%Z=%%b
																)
	if defined _MOI_localscope endlocal
	)
if defined _MOI_localscope endlocal

REM this clears the old base variable
if "[%_MOI_Input%]" NEQ "[]" for /f "tokens=1,2 delims==" %%a in ('set %_MOI_Input% 2^>nul') do if "[%_MOI_Input%]" NEQ "[%%a]" set %%a=
Call :ClearVariablesByPrefix %_MoveObjectOnly_prefix% _MoveObjectOnly 
if "[%~3]" NEQ "[]" if "[%~4]" NEQ "[]" shift & shift & GoTo :MoveObjectOnly
GoTo :EOF

Here is the test function

Code: Select all

:MoveObjectOnly-DEMO
set myarray[0]=Open Source, light and extremely simple,
set myarray[1]=It is a single executable file with no dependencies.
set myarray[2]=Just download it and add it to your PATH
set myarray[6]=Create, edit, copy, move, download your files easily,
set myarray[7]=everywhere, every time. Use it as your personal cloud.
set myarray[0].suffixA=test.sufA.0
set myarray[1].suffixA=test.sufA.1
set myarray[2].suffixA=test.sufA.2
set myarray[6].suffixA=test.sufA.6
set myarray[7].suffixA=test.sufA.7
set myarray[0].suffixB=test.sufB.0
set myarray[1].suffixB=test.sufB.1
set myarray[2].suffixB=test.sufB.2
set myarray[6].suffixB=test.sufB.6
set myarray[7].suffixB=test.sufB.7
set myarray.ubound=7

echo.&echo Printing myarray&echo.
call :echoarray myarray LINENUMBERS
echo.&call :echoarray myarray .suffixA LINENUMBERS
echo.&call :echoarray myarray .suffixB LINENUMBERS

echo.&echo Moving object [6] to [4]&echo.
call :moveobjectonly myarray[6] myarray[4]

echo.&echo Printing myarray&echo.
call :echoarray myarray LINENUMBERS
echo.&call :echoarray myarray .suffixA LINENUMBERS
echo.&call :echoarray myarray .suffixB LINENUMBERS

echo.&echo Moving object [4] to [3]&echo.
call :moveobjectonly myarray[4] myarray[3]

echo.&echo Printing myarray&echo.
call :echoarray myarray LINENUMBERS
echo.&call :echoarray myarray .suffixA LINENUMBERS
echo.&call :echoarray myarray .suffixB LINENUMBERS

echo.&echo Moving objects [1][2][3] to [4][5][6]&echo.
call :moveobjectonly myarray[1] myarray[4]
call :moveobjectonly myarray[2] myarray[5]
call :moveobjectonly myarray[3] myarray[6]

echo.&echo Printing myarray&echo.
call :echoarray myarray LINENUMBERS
echo.&call :echoarray myarray .suffixA LINENUMBERS
echo.&call :echoarray myarray .suffixB LINENUMBERS

GoTo :EOF

jeb
Expert
Posts: 1042
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: GOTO inside FOR loop ? Can it be done ?

#3 Post by jeb » 18 Sep 2023 02:42

As you have already observed, GOTO breaks any loop, it can't be used for continue, but for break.

For CONTINUE, in most cases I would recommend an IF block instead.

Code: Select all

FOR /L (1 1 10) DO (
  if someCondition (
    echo DoWork
  ) ELSE ( DoNothing )
)
If your code is very complex/long you could use GOTO's for the continue by using a subfunction

Code: Select all

FOR /L (1 1 10) DO (
  call :workerFunc
)
...

:workerFunc
...
if someContinueCondition goto :continue
...
if someotherContinueCondition goto :continue
...
:continue
exit /b

shodan
Posts: 54
Joined: 01 May 2023 01:49

Re: GOTO inside FOR loop ? Can it be done ?

#4 Post by shodan » 18 Sep 2023 19:41

Thanks,

I'm not sure exactly where or why, but sometimes I use a IF inside a FOR loop and it behaves unexpectedly, especially in loops that go in and out of delayedexpansion local scopes

It seems making function calls will be the cleanest way forward

It would have been great to have a way to just "skip to next iteration" of the for loop, I used that a few times in VBA, put a :next_iteration before the "next" statement.

Post Reply