Page 1 of 1

GOTO inside FOR loop ? Can it be done ?

Posted: 17 Sep 2023 16:45
by shodan
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 ?

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

Posted: 17 Sep 2023 17:07
by shodan
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

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

Posted: 18 Sep 2023 02:42
by jeb
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

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

Posted: 18 Sep 2023 19:41
by shodan
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.