【问题标题】:Batch File Nested For /L Loops and Exiting them为 /L 循环嵌套的批处理文件并退出它们
【发布时间】:2015-08-25 21:54:27
【问题描述】:
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
)

这段代码没有像我期望的那样通过 for 循环。我试图做这样的事情 How to break inner loop in nested loop batch script ,但无济于事。有什么建议吗?

编辑

当可执行文件完成运行时,ERRORLEVEL 不为 1。

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

【问题讨论】:

  • 在您编辑的代码中,您需要使用!ERRORLEVEL! 而不是%ERRORLEVEL%!此外,您根本没有查询 !ERRORLEVEL! in the outer for` 循环。您可以测试call :innerloop %%a || taskkill /f /im %%a.exe 是否有效(意味着taskkillcall 返回非零ERRORLEVEL 时执行)。

标签: batch-file for-loop nested


【解决方案1】:

for 循环内执行goto 命令会中断循环迭代。这适用于 所有 嵌套的 for 循环。

但是,您可以将您的内部 for 循环移动到子例程中(使用 call 调用它)。这个循环可以被goto命令终止,但是从子程序返回后,剩余的(外层)for循环不受影响,继续迭代。

@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

在这里,我将内部for 循环移动到一个子例程:SUBLOOP,由call :SUBLOOP %%a.exe 从其以前的代码部分调用,将当前值从外部传递给进程(可执行文件%%a.exefor 循环到它。 ERRORLEVEL 在终止时转移到调用例程。
:SUBLOOP返回到外部for循环后,检查ERRORLEVEL是否为零,如果是,则杀死当前迭代的可执行程序的进程。 && 是一个条件命令分隔符,它仅在第一个命令成功时执行第二个命令,也就是说,如果 ERRORLEVEL 被清除。
请注意外部for 循环之后的exit /B 命令,这是在外部循环完成后不无意执行SUBLOOP 部分所必需的。

注意:
goto 不会立即终止for 循环迭代;实际上,每个待处理的迭代仍然会被静默计数,但不会再执行 for 正文的命令。因此,具有大量迭代的循环可能需要相当长的时间才能继续执行批处理。谢谢@foxidrive 这个useful information
此外,如果goto 目标标签放置在for 正文中,则在循环完全完成后继续执行该点。然后,该点之后的任何代码都被视为“正常”代码,而不是 for 正文的一部分。

【讨论】:

  • 如果一个仍在运行,此代码会导致可执行文件相互重叠。
  • 对不起最后一件事。添加/WAIT 开关时output_%%a.txt 里面没有任何文本。有什么办法可以解决这个问题?
  • 为什么我还需要/WAIT?存在内部 for 循环是为了在一个小时过去或可执行文件完成执行之前不允许外部循环运行。 @aschipfl
  • @aschipfl goto 命令不会立即终止 for 循环 - 它会默默地计算尚未执行的每一遍。尝试 for loop 数到 1000 万,并在达到 5000 时使用 goto 看看会发生什么。
  • @aschipfl 是的,它会停止处理命令 - 但你写了loop immediately terminates iteration 但它没有,因为在继续批处理脚本之前,长循环可能会再运行 15 分钟等。您的描述应该改写为,可以想象一个循环在批处理继续之前可能需要相当长的时间。有一些技术可以立即退出for loop,但这不是其中之一。
猜你喜欢
  • 2013-07-13
  • 2011-10-07
  • 2011-05-19
  • 1970-01-01
  • 2017-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多