Reputation: 3216
I've seen other questions that address parts of this problem, but I didn't see a solution that accomplished the whole thing. There's no such thing as "while" in the command processor of course, and since goto :line statements breaks out of all loops, it's not an option for iterating over some set of values for a specific duration before proceeding to the next value.
Here is pseudo-code for the logical flow I'm going for; the command processor has foiled my attempts to make it run so far. How would you construct this (besides ditching batch scripts and going to c# or something)?
Pseudocode:
SET %%durationMinutes=60
FOR %%X IN (10 20 40 80 160 200 0) DO (
:: calculate elapsed minutes...
WHILE %elapsedMinutes < %%durationMinutes DO (
:: unrelated hocus pocus here, uses %%X as a variable
call :foo %%X
// can't use goto to simulate the WHILE loop since it breaks %%X, so...?
)
)
Upvotes: 3
Views: 8796
Reputation: 67216
This problem have two faces. In first place, the fact that a goto
break any nesting IF/FOR command, but perhaps more important is the fact that a While assembled with goto
is very slow. One solution is to simulate a while with an endless loop: for /L %%i in () do ...
and break it via a goto
in a subroutine. The problem with this solution is that a for /L
can NOT be broken with goto
in the same cmd.exe context. So, the solution is to invoke a new cmd.exe just to execute the While.
The Batch file to execute in the new cmd.exe may be the same caller file, so we need control the execution of the While via special parameters in the same Batch file. Also, we may use auxiliary variables to make clearer all thus stuff. Here it is:
@echo off
setlocal EnableDelayedExpansion
rem While dispatcher
if "%1" equ "While" goto %2
rem Definition of auxiliary variables
set While=for /L %%a in () do if
set Do=(
set EndW=) else exit
set RunWhile=cmd /Q /C "%0" While
echo Example of While
echo/
goto RunMyWhile
rem Write the While code here
:MyWhile
set /A i=0, num=0
set /P "num=Enter number: "
%While% !num! gtr 0 %Do%
set /A i+=1
echo !i!- Number processed: !num!
echo/
set num=0
set /P "num=Enter number: "
%EndW% !i!
rem Execute the While here
:RunMyWhile
%RunWhile% MyWhile
set i=%errorlevel%
echo/
echo While processed %i% elements
As you see, the While may return a numeric result to the caller code via ERRORLEVEL.
In your particular case:
SET durationMinutes=60
goto RunMyWhile
:MyWhile
:: calculate elapsed minutes...
%WHILE% !elapsedMinutes! < %durationMinutes% %DO%
:: unrelated hocus pocus here, uses %3 as a variable
call :foo %3
:: calculate elapsed minutes again...
%EndW%
:RunMyWhile
FOR %%X IN (10 20 40 80 160 200 0) DO (
%RunWhile% MyWhile %%X
)
This topic is explained with detail at this post
Upvotes: 5
Reputation: 130819
Harry has the right idea to use a subroutine in his answer. Normally the outer loop FOR variables are not accessible once the subroutine is called. But they "magically" become available again if the subroutine has its own FOR loop. This can eliminate the need to store the outer loop values in variables or pass the values as parameters.
@echo off
for %%x in (1 2 3 4 5) do (
echo begin outer loop iteration, x=%%x
call :innerLoop
echo end of outer loop iteration, x=%%x
echo(
)
echo Outer loop complete
exit /b
:innerLoop
echo inside subroutine, x FOR variable is inaccessible: x=%%x
for %%y in (1 2 3 4 5) do (
if %%y gtr %%x goto :break
echo within FOR loop inside subroutine: x=%%x y=%%y
)
:break
echo end of subroutine, x FOR variable is inaccessible: x=%%x
exit /b
Here are the results:
begin outer loop iteration, x=1
inside subroutine, x FOR variable is inaccessible: x=%x
within FOR loop inside subroutine: x=1 y=1
end of subroutine, x FOR variable is inaccessible: x=%x
end of outer loop iteration, x=1
begin outer loop iteration, x=2
inside subroutine, x FOR variable is inaccessible: x=%x
within FOR loop inside subroutine: x=2 y=1
within FOR loop inside subroutine: x=2 y=2
end of subroutine, x FOR variable is inaccessible: x=%x
end of outer loop iteration, x=2
begin outer loop iteration, x=3
inside subroutine, x FOR variable is inaccessible: x=%x
within FOR loop inside subroutine: x=3 y=1
within FOR loop inside subroutine: x=3 y=2
within FOR loop inside subroutine: x=3 y=3
end of subroutine, x FOR variable is inaccessible: x=%x
end of outer loop iteration, x=3
begin outer loop iteration, x=4
inside subroutine, x FOR variable is inaccessible: x=%x
within FOR loop inside subroutine: x=4 y=1
within FOR loop inside subroutine: x=4 y=2
within FOR loop inside subroutine: x=4 y=3
within FOR loop inside subroutine: x=4 y=4
end of subroutine, x FOR variable is inaccessible: x=%x
end of outer loop iteration, x=4
begin outer loop iteration, x=5
inside subroutine, x FOR variable is inaccessible: x=%x
within FOR loop inside subroutine: x=5 y=1
within FOR loop inside subroutine: x=5 y=2
within FOR loop inside subroutine: x=5 y=3
within FOR loop inside subroutine: x=5 y=4
within FOR loop inside subroutine: x=5 y=5
end of subroutine, x FOR variable is inaccessible: x=%x
end of outer loop iteration, x=5
Outer loop complete
Upvotes: 3
Reputation: 36308
Just put the contents of the loop in a subroutine. Here's pseudocode for a simple example:
for x in (1 2 3 4 5) {
y = 1
while (y <= x) {
echo y
y = y + 1
}
echo Looping
}
And the implementation in batch:
for %%x in (1 2 3 4 5) do set x=%%x & call :for_loop
goto :for_end
:for_loop
set y=1
:while_loop
if not %y% LEQ %x% goto :while_end
echo %y%
set /a y=y+1
goto :while_loop
:while_end
echo Looping
goto :eof
:for_end
And the output:
1
Looping
1
2
Looping
1
2
3
Looping
1
2
3
4
Looping
1
2
3
4
5
Looping
Upvotes: 1