Call label thats inside a for loop

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Cleptography
Posts: 287
Joined: 16 Mar 2011 19:17
Location: scriptingpros.com
Contact:

Call label thats inside a for loop

#1 Post by Cleptography » 25 May 2011 12:42

I saw a post here and someone was asking if you could call a label that was inside a for loop, and it was said that it could not be done which I also thought was true. Doing some testing and I am not sure if my logic is correct so please correct me if I am wrong, but does this constitute as being able to call a label that's inside a for loop?

Code: Select all

@echo off

for /d %%a in (1 2 3 4 5) do (
   if [%%a]==[0] (
      call :INSIDE&& goto :eof
:INSIDE
echo.Inside the for loop
goto :eof
   ) else echo.%%a
)


Change 0 to 1-5 to see the logic working

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Call label thats inside a for loop

#2 Post by dbenham » 25 May 2011 15:50

Very interesting.

Most definitely the call is working - Check out the following:

Code: Select all

@echo off
cls
call :whackyLoop "Whacky Loop"
echo.
echo.
echo.
call :coolLoop "Cool Loop"
exit /b

:whackyLoop
echo before loop (%~1)
for /d %%a in (1 2 3 4 5) do (
  echo =============
  echo %%a
  if [%%a] geq [3] (
    call :INSIDE %%a
    echo before INSIDE label
    :INSIDE
      echo.Inside the loop %%a ^(%~1^)
  )
)
echo -------------
echo after loop (%~1)
exit /b

:coolLoop
echo before loop (%~1)
for /d %%a in (1 2 3 4 5) do (
  echo --------------
  echo %%a
  if [%%a] geq [3] call :INSIDE%%a %%a
  if 1==0 (
    :INSIDE3
      echo Process %%a ^(%1^)
    exit /b
    :INSIDE4
      echo Different Process %%a ^(%1^)
    exit /b
    :INSIDE5
      echo Final Process %%a ^(%1^)
    exit /b
  )
)
echo ----------------
echo after loop (%~1)
exit /b

Output:

Code: Select all

before loop (Whacky Loop)
=============
1
=============
2
=============
3
Inside the loop %a (3)
-------------
after loop (3)
before INSIDE label
Inside the loop 3 (Whacky Loop)
=============
4
Inside the loop %a (4)
-------------
after loop (4)
before INSIDE label
Inside the loop 4 (Whacky Loop)
=============
5
Inside the loop %a (5)
-------------
after loop (5)
before INSIDE label
Inside the loop 5 (Whacky Loop)
-------------
after loop (Whacky Loop)



before loop (Cool Loop)
--------------
1
--------------
2
--------------
3
Process %a (3)
--------------
4
Different Process %a (4)
--------------
5
Final Process %a (5)
----------------
after loop (Cool Loop)

The Cool Loop is well behaved, but I can not see any benefit to the technique, given that the call loses the FOR loop variable context. And I am pretty sure that it is not calling the function from memory - I think it is still scanning the batch file for each call. So all we manage to do is slow down the loop slightly with an IF that never fires. So we might as well keep the function defs outside the loop.

The Whacky Loop is just that. Maybe somebody someday will come up with some bizarre scenario where some variation of it is useful. But I'm not going to hold my breath.

Dave Benham

Cleptography
Posts: 287
Joined: 16 Mar 2011 19:17
Location: scriptingpros.com
Contact:

Re: Call label thats inside a for loop

#3 Post by Cleptography » 25 May 2011 16:29

Thanks for the response Dave. There seems to be no logical reason for using such a loop, but I guess it looks like it can at least be done for whatever purpose. The inside call seems to slow by about .02 mil with the if. Maybe someone will come up with some wacky idea for using such a loop but doubtful.

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Call label thats inside a for loop

#4 Post by Liviu » 11 Feb 2014 23:31

dbenham wrote:The Cool Loop is well behaved, but I can not see any benefit to the technique, given that the call loses the FOR loop variable context. And I am pretty sure that it is not calling the function from memory - I think it is still scanning the batch file for each call.

And here is verification that it's rescanning - apparently from the end of the block down, and then from the top down again, only hitting labels inside the block if none others are found. I know some of you will find this obvious, but I did it the hard way and doublechecked ;-) Saving the following batch file as, say, in-call.cmd, with multiple :err labels placed in various places, cmd's order of preference when resolving a 'call :err' seems to be...

Code: Select all

@setlocal enableDelayedExpansion & echo off & goto :main

:err [3]
for /f %%n in ('set/a %~1 + 3') do exit /b %%n          &rem 3rd choice

:err [4]
for /f %%n in ('set/a %~1 + 4') do exit /b %%n          &rem 4th choice

:main
echo(
for %%n in (100 200) do (
  if 1 equ 0 (
  :err [5]
    for /f %%n in ('set/a %~1 + 5') do exit /b %%n      &rem 5th choice
  )
  call :err %%n
  echo [%%n]  !errorlevel!
  call :err %%n
  echo [%%n]  !errorlevel!
  if 1 equ 0 (
  :err [6]
    for /f %%n in ('set/a %~1 + 6') do exit /b %%n      &rem 6th choice
  )
)

set "@err=:err"
echo(
call; & call %@err%
echo call %%@err%% = call %@err% --^> errorlevel !errorlevel!
call; & call !@err!
echo call ^^!@err^^! = call !@err! --^> errorlevel !errorlevel!

%@err%                                                  &rem never call'd
!@err!
echo(
echo *** label injection failed ^^!? *** [%0]

endlocal & goto :eof

:err [1]
for /f %%n in ('set/a %~1 + 1') do exit /b %%n          &rem 1st choice

:err [2]
for /f %%n in ('set/a %~1 + 2') do exit /b %%n          &rem 2nd choice
Output is...

Code: Select all

C:\tmp>in-call

[100]  101
[100]  101
[200]  201
[200]  201

call %@err% = call :err --> errorlevel 1
call !@err! = call :err --> errorlevel 1

*** label injection failed !? *** [in-call]

C:\tmp>

Points of notice:
- the output verifies the first-choice :err when all are present, the rest can be verified by commenting out the preferred choice one at a time, and looking at the last digit of the returned errorlevel;
- the consecutive calls inside the for loop block show that labels are not re-targeted during execution of a loop;
- the two iterations of the for loop show that labels are not re-targeted after each step.

Finally, and mostly unrelated, but it seems that labels cannot be "injected" in a batch via macros - as the @err attempts failed. If _that_ part worked, I might have thought of a "bizarre scenario where some variation of it is useful". But you were right not to hold your breath ;-)

Liviu

Aacini
Expert
Posts: 1921
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Call label thats inside a for loop

#5 Post by Aacini » 12 Feb 2014 02:35

This is what I know about these types of combinations:

When a GOTO command is executed inside any block in parentheses:
  1. All pending active commands, like FOR's and IF's, are cancelled (excepting the FOR /L that, although the index continue incrementing until the end limit, no commands are executed after the GOTO).
  2. The control is transfered to the target label.
  3. The program continue executing from this point on. Any closing parentheses found after the target label are ignored.

When a GOTO command is executed the target label is searched from the following line until the end of the file. If the label is not found, the search continue from the beginning of the file until the line of the original GOTO; at that point the "Label not found" error is issued. The first label found using this scheme is the target of the GOTO. This mechanism allows to use repeated labels to perform "short jumps" in forward direction.

Antonio

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: Call label thats inside a for loop

#6 Post by Liviu » 12 Feb 2014 10:49

Aacini wrote:When a GOTO command is executed the target label is searched from the following line until the end of the file. If the label is not found, the search continue from the beginning of the file until the line of the original GOTO; at that point the "Label not found" error is issued. The first label found using this scheme is the target of the GOTO.

Right, with the observation that the search is in terms of parsed/logical lines, not physical lines - for example a parenthesized block counts as one logical line.

The same applies to call (example in the previous post) and goto...

Code: Select all

@setlocal enableDelayedExpansion & echo off

(
  if 1 equ 0 (
  :err [2]
    echo err [2] & goto :eof
  )
  goto :err
  if 1 equ 0 (
  :err [3]
    echo err [3] & goto :eof
  )
)

:err [1]
echo err [1] & goto :eof

endlocal & goto :eof

The only case when ':err [3]' is targeted is if the other two ':err' labels are deleted. In other words, the interpreter looks everywhere else first, before using the label just under its nose, in the very block it executes ;-)

Liviu

Aacini
Expert
Posts: 1921
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Call label thats inside a for loop

#7 Post by Aacini » 12 Feb 2014 11:22

Liviu wrote:
Aacini wrote:When a GOTO command is executed the target label is searched from the following line until the end of the file. If the label is not found, the search continue from the beginning of the file until the line of the original GOTO; at that point the "Label not found" error is issued. The first label found using this scheme is the target of the GOTO.

Right, with the observation that the search is in terms of parsed/logical lines, not physical lines - for example a parenthesized block counts as one logical line.

Liviu

Interesting! I had not realized that...

Code: Select all

@echo off

set true=1 equ 1
if %true% (
   goto dest
   :dest
   echo not here
   if %true% (
      :dest
      echo not here
      if %true% (
         :dest
         echo not here
         if %true% (
            :dest
            echo not here
         )
      )
   )
)

:dest
echo BUT HERE!


Antonio

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

Re: Call label thats inside a for loop

#8 Post by jeb » 13 Feb 2014 03:00

The point is that the parser file position is behind the block.

When a call inside a block will be executed, the search starts at the parser file position and not after the line with the CALL.

When the label itself is inside the block the parser has to search first to the end of the file and then restarts the search from the beginning, effectivly the complete file is scanned then.

Code: Select all

@echo off

goto :block

:Label1
echo Label1 at Line4
exit /b

:block
(
  call :label1
  call :label2
  exit /b
 
  :label1
  echo Label1 inside the block
  exit /b
 
  :label2
  echo Label2 inside the block
  exit /b
)


Output wrote:Label1 at Line4
Label2 inside the block


When the call is executed the parser starts just after the label line, ignoring any multiline carets

Code: Select all

@echo off

echo call the label
call :label
echo Returned from call

echo Jump into the function directly
:label This is a multiline ^
echo This will be displayed only when the label is called
echo This line will be displayed always
echo(
exit /b


jeb

Post Reply