2
SETLOCAL ENABLEDELAYEDEXPANSION
set list=A B C D E

FOR %%a IN (%list%) DO (
    start %%a.exe > ouput_%%a.txt
    echo 120, 30 second loops = 60 min
    FOR /L %%i IN (1,1,120) DO (
        REM pause for 30 seconds
        ping 1.1.1.1 -n 1 -w 30000 > nul

        echo find the running executable
        tasklist | findstr %%a.exe > nul
        REM !ERRORLEVEL!
        REM exit the script if no executable is found (i.e it has run successfully)
        if !ERRORLEVEL! equ 1 goto success
   )
   REM kill executable if we haven't exited yet
   taskkill /f /im %%a.exe
   :success
)

This code doesn't go through the for loops as I expect them to. I tried to do something like this How to break inner loop in nested loop batch script , but to no avail. Any advice?

EDIT

ERRORLEVEL is not 1 when an executable has finished running.

SETLOCAL ENABLEDELAYEDEXPANSION
set list=A B C D E

FOR %%a IN (%LIST%) DO (
    start %%a.exe > ouput_%%a.txt
    REM 120, 30 second loops = 60 min
    call :innerloop %%a
    REM kill executable if we haven't exited yet
    taskkill /f /im %%a.exe
)
goto :eof
:innerloop
FOR /L %%i IN (1,1,120) DO (
    REM pause for 30 seconds
    ping 1.1.1.1 -n 1 -w 30000 > nul

    REM find the running executable
    tasklist | findstr %%a.exe > nul
    REM !ERRORLEVEL!
    REM exit the script if no executable is found (i.e it has run successfully)
    if %ERRORLEVEL% equ 1 goto :next
)
:next
goto :eof
Community
  • 1
  • 1
emilk
  • 75
  • 1
  • 3
  • 10
  • 1
    In your EDITed code, you need to use `!ERRORLEVEL!` rather than `%ERRORLEVEL%`! Furthermore, you are not at all querying `!ERRORLEVEL! in the outer `for` loop. You could test if `call :innerloop %%a || taskkill /f /im %%a.exe` works (meaning `taskkill` is _only_ executed if `call` returns a non-zero `ERRORLEVEL`). – aschipfl Aug 25 '15 at 23:01

1 Answers1

2

Executing the goto command within a for loop breaks up the loop iteration. This is true for all nested for loops.

However, what you can do is moving your inner for loop into a sub-routine (call it using call). This loop can be terminated by a goto command, but after returning from the sub-routine, the remaining (outer) for loop is not affected and continues iteration.

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
set list=A B C D E

FOR %%a IN (%list%) DO (
    start %%a.exe > ouput_%%a.txt
    echo 120, 30 second loops = 60 min
    REM kill executable if we haven't exited yet
    call :SUBLOOP %%a.exe && taskkill /f /im %%a.exe
)
exit /B

:SUBLOOP
FOR /L %%i IN (1,1,120) DO (
    REM pause for 30 seconds
    timeout /T 30 /NOBREAK > nul
    echo find the running executable
    tasklist | findstr /I /B "%~1" > nul
    REM !ERRORLEVEL! is 0 if found and 1 otherwise
    REM exit the script if no executable is found
    REM (i.e it has run successfully)
    if !ERRORLEVEL! equ 1 goto :EOF
)
exit /B 0

Herein I moved the inner for loop into a sub-routine :SUBLOOP, which is called from its former code section by call :SUBLOOP %%a.exe, passing the current value to process (the executable %%a.exe) from the outer for loop to it. ERRORLEVEL is transferred to the calling routine upon termination.
After returning from :SUBLOOP back to the outer for loop, it is checked whether or not ERRORLEVEL is zero, and if so, the process of the currently iterated executable is killed. The && is a conditional command separator which executes the second command only if the first one succeeded, that is, if ERRORLEVEL is cleared.
Note the exit /B command after the outer for loop which is necessary to not execute the SUBLOOP section unintentionally after the outer loop has been completed.

Note:
goto does not terminate the for loop iteration immediately; in fact every pending iteration is still counted silently, but no more commands of the for body are executed. A loop with a huge number of iterations can therefore take a considerable amount of time before batch execution continues. Thank you @foxidrive for this useful information!
Moreover, if the goto target label is placed within the for body, execution continues at that point after total completion of the loop. Any code following that point is then treated as "normal" one not part of a for body.

Community
  • 1
  • 1
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • This code is causing the executables to overlap each other if one is still running. – emilk Aug 25 '15 at 23:10
  • Sorry one last thing. When adding the `/WAIT` switch `output_%%a.txt` does not have any text inside of it. What can be done to fix this? – emilk Aug 25 '15 at 23:25
  • Also why do I need the `/WAIT`? The inner for loop is there in order to not allow the outer loop to run until an hour has passed or the executable has finished executing. @aschipfl – emilk Aug 25 '15 at 23:33
  • I agree, `/WAIT` sould not be required... I just [corrected an issue](http://stackoverflow.com/revisions/32215198/4) in `:SUBLOOP` (there was `%2` instead of `%1` erroneously)... – aschipfl Aug 25 '15 at 23:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87937/discussion-between-emilk-and-aschipfl). – emilk Aug 25 '15 at 23:52
  • 1
    @aschipfl A `goto` command doesn't immediately terminate a for loop - it silently counts every pass that is yet to go. Try a `for loop` counting up to 10 million and use a goto when it gets to 5000 to see what happens. – foxidrive Aug 26 '15 at 00:48
  • @foxidrive, let's say it that way: `goto` breaks `for` and makes its variable (for instance `%%a`) non-accessible; for the batch script execution `for` looks terminated; – aschipfl Aug 26 '15 at 01:17
  • 1
    @aschipfl yes, it stops processing the commands - but you wrote `loop immediately terminates iteration` and it doesn't, because a long loop could keep running for 15 more minutes etc before continuing the batch script. Your description should be reworded to say that a loop can conceivably take considerable time before the batch continues. There are techniques to immediately exit a `for loop` but this isn't one of them. – foxidrive Aug 26 '15 at 03:28
  • Please see my [edit](http://stackoverflow.com/revisions/32215198/5), @foxidrive; I hope everything is correct now; thank you for the very interesting information! – aschipfl Aug 26 '15 at 11:44
  • @emilk, I edited the code (twice actually, see [rev6](http://stackoverflow.com/revisions/32215198/6) and [rev7](http://stackoverflow.com/revisions/32215198/7)); summary of changes: 1. I used the wrong separator `||` instead of `&&`; 2. `findstr` now works case-**in**sensitive (like `start`); 3. I replaced `ping` by `timeout` which is intended for delays (if you're working on Windows XP you'll need to go back to your `ping 1.1.1.1 -n 1 -w 30000`); – aschipfl Aug 27 '15 at 19:59