View unanswered posts | View active topics It is currently 25 Nov 2014 20:46



Post new topic Reply to topic  [ 33 posts ]  Go to page 1, 2, 3  Next
infinite loop with break condition 
Author Message
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post infinite loop with break condition
Hi everybody.

Sometimes I need an infinite loop that breaks if a specific value is reached. The only way I get it work is via GOTO.
Code:
@prompt $g$s &setlocal &set /a n=0
:loop
  set /a n+=1
  echo #%n%
  >nul ping -n 2 localhost
  if %n%==10 goto exitloop
goto loop
:exitloop
pause

But the backward searching for the label is slow.

Another way to create an infinite loop is using FOR /L. That's much faster but I can't find a way to break it.
This doesn't work:
Code:
@prompt $g$s &setlocal EnableDelayedExpansion &set /a n=0
for /l %%i in () do (
  set /a n+=1
  echo #!n!
  >nul ping -n 2 localhost
  if !n!==10 goto exitloop
)
:exitloop
pause

Is anybody out there who found a better way?

Regards
aGerman


27 Dec 2011 17:35
Profile
Expert

Joined: 12 Feb 2011 21:02
Posts: 1275
Location: United States (east coast)
Post Re: infinite loop with break condition
I've tried to do the same in the past, and never came up with a better solution.

I suppose if you are worried about disk thrashing while you wait for the exit condition to become true, you could try a hybrid approach.
Code:
:loop
for /l %%N in (1 1 10000) do (
  >nul ping -n 2 localhost
  if ***exit condition is true*** goto exitLoop
)
goto loop
:exitLoop

Now the test is done almost entirely from memory. The :loop label is scanned only once every 10000 iterations. The delay is small once the exit condition is reached since the DO clause is ignored after executing the GOTO, and the FOR can count to 10000 very fast.

Dave Benham


27 Dec 2011 18:19
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post Re: infinite loop with break condition
Hi Dave,

thanks for your reply.
I admit this ping example was senseless (only to show what happen if you try to exit the for /L loop).

Better example:
Some days ago in a German forum somebody came up with the question how to calculate the least common multiple of two numbers. There is a simple algorithm but it needs some kind of WHILE loop.
Code:
@echo off &setlocal
call :lcm 3527 3784 var
echo %var%
pause
goto :eof

:lcm
setlocal
set /a "i=%1", "j=%2" 2>nul ||(endlocal &goto :eof)
:__lcm
if %j% neq 0 (
  set /a k=j, j=i%%j, i=k
  goto __lcm
)
set /a "j=%1*%2/i" 2>nul ||(endlocal &goto :eof)
endlocal &if "%~3" neq "" set /a "%~3=%j%"
goto :eof

Hence I'm basically looking for a WHILE equivalent. But you confirmed my fears that only GOTO is suitable to create a similar loop.

Regards
aGerman


27 Dec 2011 18:58
Profile

Joined: 23 Dec 2011 13:59
Posts: 2016
Post Re: infinite loop with break condition
Was reading something over at Stack Overflow.

This creates your infinite loop with a break but is also kills your batch file. Which would also be pointless.
Code:
@echo off & setlocal EnableDelayedExpansion
set /a num=0
for /L %%N in (1 0 10) do (
   set /a num+=1
   CALL :loop !num!
)

:__loop
()

:loop
IF "%~1"=="10" call :__loop 2>nul


28 Dec 2011 08:48
Profile

Joined: 23 Dec 2011 13:59
Posts: 2016
Post Re: infinite loop with break condition
I was reading this over at stack overflow again.
http://stackoverflow.com/a/6730214

Would this work for you.
Code:
@echo off & setlocal EnableDelayedExpansion
if "%1"=="loop" (
   set /a num=0
   for /l %%f in (1 0 10) do (
      set /a num+=1
      IF "!num!"=="500" echo.>%%f.tmp
      if exist %%f.tmp exit
  )
  goto :eof
)
cmd /v:on /q /d /c "%0 loop"
echo done


28 Dec 2011 10:44
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post Re: infinite loop with break condition
Well, the first is not applicable. The second looks interesting, but the question is how to "rescue" the value of a variable?
Code:
@echo off & setlocal EnableDelayedExpansion
if "%1"=="loop" (
   set /a num=0
   for /l %%f in (0) do (
      set /a num+=1
      echo !num!
      IF "!num!"=="500" for /f %%g in ("!num!") do (endlocal & echo %%%% g=%%g & set "num=%%g" & exit)
  )
)
call cmd /c "%~sf0 loop"
echo num=%num%
pause

Hmm...

Thanks anyway, perhaps somebody knows how to get it to work.

Regards
aGerman


28 Dec 2011 11:07
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 776
Location: México City, México
Post Re: infinite loop with break condition
There is no way to break a FOR loop in the same CMD context, so I used a trick to export the cycle to a new CMD that can be broken via an EXIT command, and the execution return to the caller code:
Code:
@echo off
setlocal DisableDelayedExpansion
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

echo %\n%
@echo off%\n%
set num=0%\n%
for /L %%%%i in (1,0,1) do (%\n%
   set /A num+=1%\n%
   echo !num!%\n%
   if !num! == 10 (%\n%
      call break%\n%
   )%\n%
) > while.bat

echo exit > break.bat

echo Call the while:
cmd /c while
echo Return from while

In previous example I created the WHILE.BAT in the same caller program (using %\n% trick), but it can be a completely separated Batch file.

We may define a generic and more readable WHILE this way:
Code:
echo %\n%
@echo off%\n%
set num=1%\n%
for /L %%%%i in (1,0,1) do (%\n%
   call break %%1 %%2 %%3%\n%
   echo !num!%\n%
   set /A num+=1%\n%
) > while.bat

echo if not %%1 %%2 %%3 exit > break.bat

echo Call the while:
cmd /c while %%num%% leq 10
echo Return from while

However, the only way to get back a value from the cycle is via a disk file (I wonder if a macro could directly do this).


28 Dec 2011 13:39
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post Re: infinite loop with break condition
Hi Aacini.

Thanks, I'm gonna test it.

Aacini wrote:
However, the only way to get back a value from the cycle is via a disk file

You CAN get back a value. But only a single integer via errorlevel.
Code:
@echo off & setlocal EnableDelayedExpansion
if "%1"=="loop" (
   set /a num=0
   for /l %%f in (0) do (
      set /a num+=1
      echo !num!
      IF "!num!"=="100" for /f %%g in ("!num!") do (endlocal & echo %%%% g=%%g & exit %%g)
  )
)
cmd /c "%~f0" loop
echo ERRORLEVEL=%errorlevel%
pause


Regards
aGerman


28 Dec 2011 13:52
Profile
Expert

Joined: 12 Feb 2011 21:02
Posts: 1275
Location: United States (east coast)
Post Re: infinite loop with break condition
aGerman wrote:

Aacini wrote:
However, the only way to get back a value from the cycle is via a disk file

You CAN get back a value. But only a single integer via errorlevel.

Or you can parse the output of your CMD statement using FOR /F %%A IN ('CMD....') DO ....


Dave Benham


28 Dec 2011 14:31
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post Re: infinite loop with break condition
Thanks Dave :!: , that's it :D

Code:
@echo off & setlocal EnableDelayedExpansion
if "%1"=="loop" (
   set /a num=0
   for /l %%f in (0) do (
      set /a num+=1
      IF "!num!"=="100" (echo(!num!&endlocal&exit)
  )
)
for /f "delims=" %%i in ('cmd /c "%~f0" loop') do set "num=%%i"
echo num=%num%
pause


Regards
aGerman


28 Dec 2011 14:57
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 776
Location: México City, México
Post Re: infinite loop with break condition
aGerman wrote:
You CAN get back a value. But only a single integer via errorlevel.

Yes, it works!
Code:
echo %\n%
@echo off%\n%
set num=0%\n%
set result=0%\n%
for /L %%%%i in (1,0,1) do (%\n%
   set /A num+=1%\n%
   if not %%1 %%2 %%3 call break !result!%\n%
   echo !num!%\n%
   set /A result+=num%\n%
) > while.bat

echo exit %%1 > break.bat

setlocal EnableDelayedExpansion

echo Call the while:
cmd /c while ^^^!num^^^! leq 10
echo Return from while: %ERRORLEVEL%
Code:
Call the while:
1
2
3
4
5
6
7
8
9
10
Return from while: 55


28 Dec 2011 16:31
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post Re: infinite loop with break condition
It's not necessary to create additional files.
Try:
Code:
@echo off
if /i "%~1"=="while" call :while %*
cmd /c "%~f0" while num leq 10
echo Return from while: %ERRORLEVEL%
pause
goto :eof

:while
setlocal EnableDelayedExpansion
set num=0
set result=0
for /L %%i in (0) do (
  set /A num+=1
  if not !%~2! %~3 %~4 exit !result!
  echo !num!
  set /A result+=num
)


Regards
aGerman


28 Dec 2011 21:16
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 776
Location: México City, México
Post Re: infinite loop with break condition
I need the help of a Batch macro expert :!:

I tried to write a macro, called WHILE, to make good use of this trick in an easier way. I first wrote a subroutine that works ok. When I convert it to a macro I got an error in the line with the REM below:
Code:
@echo off
setlocal DisableDelayedExpansion
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

set while=for %%n in (1 2) do if %%n==2 (%\n%
      call :StrLen argv argvLen=%\n%
      set "body=!argv:*do=!"%\n%
      call :StrLen body bodyLen=%\n%
      set /A condLen=argvLen-bodyLen-2%\n%
      for %%a in (!condLen!) do set "cond=!argv:~0,%%a!"%\n%
REM   echo for /L %%%%w in (1,0,1) do if !cond! !body! else call whileBreak^> whileBody.bat%\n%
      echo for /L %%%%w in (1,0,1  do if !cond! !body! else call whileBreak^> whileBody.bat%\n%
      echo exit %%whileResult%%^> whileBreak.bat%\n%
      cmd /Q /C whileBody%\n%
      endlocal ^& set whileResult=^!errorlevel^!%\n%
   ) else setlocal EnableDelayedExpansion ^& set argv=

set !=^^^^!

setlocal EnableDelayedExpansion

set num=1
set whileResult=0
%while% %!%num%!% leq 10 do (%\n%
   echo %!%num%!%%\n%
   set /A whileResult+=num%\n%
   set /A num+=1%\n%
)
echo While result: %whileResult%
goto :EOF

:StrLen string [result=[adjust]]
setlocal EnableDelayedExpansion
set str=%1
set str=!str:"= !
if "!str:~0,1!" == " " (
    set "str=0%~1"
) else (
    set "str=0!%1!"
)
set len=0
for /L %%A in (12,-1,0) do (
   set /A "len|=1<<%%A"
   for %%B in (!len!) do if "!str:~%%B,1!" == "" set /A "len&=~1<<%%A"
)
for %%v in (!len!) do endlocal&if not "%2" == "" (set /A "%2=%%v%3") else echo %%v
exit /B

The error is "do was unexpected at this time". I made several tests and discovered that the right parentheses of the "FOR ... IN (1,0,1)" is really closing THE IF %%N==2 ( of the macro definition :!: The line below the FOR don't cause the error, but the whileBody is not completed correctly.

I can't discover the cause of this error. HELP ME, PLEASE! :(


28 Dec 2011 22:02
Profile
Expert

Joined: 12 Feb 2011 21:02
Posts: 1275
Location: United States (east coast)
Post Re: infinite loop with break condition
I think you have discovered the problem, you just haven't figured out the solution. :wink:

You just need to make sure the ) is escaped after the macro is defined. So you need to escape the escape when defining the macro.

I think this will get you past your immediate problem.
Code:
      echo for /L %%%%w in (1,0,1^^) do if !cond! !body! else call whileBreak^> whileBody.bat%\n%


I can't figure out how your %while% "call" works :?


Dave Benham


28 Dec 2011 22:32
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1803
Location: Germany
Post Re: infinite loop with break condition
I also can't find out how it should work.

I tried a different way (with finally 3 macros).
Syntax:
cmd /c "%~f0" label c1 c2 c3 RetVarName [VarName1 Value1 [VarNameN ValueN]]
label - sub routine where the While loop is placed
c1 - variable name that should be compared
c2 - compare operator
c3 - value to compare with c1
RetVarName - variable name of the value that should be returned via errorlevel
VarName1 Value1 - variable name and value pairs for predefined variables used in the While loop

I'm stumbling upon a strange fault (see the REM line). Message: '"!_c2!" was unexpected at this time'
In this variable the compare operator will be saved, but not at the time when the macro variabe shall be assigned. In the next line I replaced !_c2! with lss and it works just fine.

It seems the cmd is parsing the syntax of IF even if it is not executed yet.
Is there a way to avoid that "early parsing" by any kind of escape sequence?

Here's the code:
Code:
@echo off &setlocal DisableDelayedExpansion

%$initWhile% :test

call :macros

:::::::::::::::::::::::::::::::::::::::::::::::::

cmd /c "%~fs0" test num lss 50 ret num 5 ret 6
echo %errorlevel%
pause
goto :eof

:test
%$While%
  set /a num+=1
  set /a ret+=num
  echo !num! !ret!
%$Wend%

:macros
set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"&rem TWO EMPTY LINES ABOVE REQUIRED!

set $initWhile=for /l %%I in (1 1 2) do if %%I==2 (%\n%
  if defined _arg (%\n%
    for /f %%J in ("!_arg!") do endlocal^&call %%J %%*%\n%
  ) else exit%\n%
) else setlocal EnableDelayedExpansion ^&set _arg=

set $While=(%\n%
  setlocal EnableDelayedExpansion%\n%
  call set "_args=%%*"%\n%
  if "!_args!"=="" exit 1%\n%
  for %%J in (!_args:* ^^=!) do if not defined _c1 (set "_c1=%%J") else (%\n%
    if not defined _c2 (set "_c2=%%J") else (%\n%
      if not defined _c3 (set "_c3=%%J") else (%\n%
        if not defined _ret (set "_ret=%%J") else (%\n%
          if not defined _var (set "_var=%%J") else (set "!_var!=%%J"^&set "_var=")%\n%
  ))))%\n%
  for /l %%J in (0) do (%\n%
    for /f "tokens=1,2" %%K in ("!_c1! !_ret!") do (%\n%
REM   if not !%%K! !_c2! !_c3! exit !%%L!%\n%
      if not !%%K! lss !_c3! exit !%%L!%\n%
    )%\n%

 
set $Wend=))

goto :eof

Regards
aGerman


29 Dec 2011 08:59
Profile
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 33 posts ]  Go to page 1, 2, 3  Next


Who is online

Users browsing this forum: Baidu [Spider], Bing [Bot], Google [Bot], Honguito98, ShadowThief, Yahoo [Bot] and 14 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Forum style by Vjacheslav Trushkin for Free Forums/DivisionCore.