DosTips.com

A Forum all about DOS Batch
It is currently 30 Aug 2016 01:19

All times are UTC-06:00




Post new topic  Reply to topic  [ 33 posts ]  Go to page 1 2 3 Next
Author Message
PostPosted: 27 Dec 2011 17:35 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
PostPosted: 27 Dec 2011 18:19 
Offline
Expert

Joined: 12 Feb 2011 21:02
Posts: 1689
Location: United States (east coast)
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


Top
   
PostPosted: 27 Dec 2011 18:58 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
PostPosted: 28 Dec 2011 08:48 
Offline
Expert

Joined: 23 Dec 2011 13:59
Posts: 3169
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


Top
   
PostPosted: 28 Dec 2011 10:44 
Offline
Expert

Joined: 23 Dec 2011 13:59
Posts: 3169
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


Top
   
PostPosted: 28 Dec 2011 11:07 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
PostPosted: 28 Dec 2011 13:39 
Offline
Expert

Joined: 06 Dec 2011 22:15
Posts: 1184
Location: México City, México
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).


Top
   
PostPosted: 28 Dec 2011 13:52 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
PostPosted: 28 Dec 2011 14:31 
Offline
Expert

Joined: 12 Feb 2011 21:02
Posts: 1689
Location: United States (east coast)
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


Top
   
PostPosted: 28 Dec 2011 14:57 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
PostPosted: 28 Dec 2011 16:31 
Offline
Expert

Joined: 06 Dec 2011 22:15
Posts: 1184
Location: México City, México
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


Top
   
PostPosted: 28 Dec 2011 21:16 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
PostPosted: 28 Dec 2011 22:02 
Offline
Expert

Joined: 06 Dec 2011 22:15
Posts: 1184
Location: México City, México
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! :(


Top
   
PostPosted: 28 Dec 2011 22:32 
Offline
Expert

Joined: 12 Feb 2011 21:02
Posts: 1689
Location: United States (east coast)
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


Top
   
PostPosted: 29 Dec 2011 08:59 
Offline
Expert

Joined: 22 Jan 2010 18:01
Posts: 2233
Location: Germany
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


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 33 posts ]  Go to page 1 2 3 Next

All times are UTC-06:00


Who is online

Users browsing this forum: Bing [Bot], ShadowThief, Yahoo [Bot] and 6 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® Forum Software © phpBB Limited