退出批处理文件中的 FOR 循环?
Posted
技术标签:
【中文标题】退出批处理文件中的 FOR 循环?【英文标题】:Exiting out of a FOR loop in a batch file? 【发布时间】:2011-10-07 09:54:38 【问题描述】:为什么这个批处理文件永远不会跳出循环?
For /L %%f In (1,1,1000000) Do @If Not Exist %%f Goto :EOF
Goto :EOF
不应该跳出循环吗?
编辑:
我想我应该更明确地问... 如何我可以跳出循环?
【问题讨论】:
你指的是DOS批处理文件还是其他批处理文件?另外,你是如何运行它的? 为什么%%f
不存在?
【参考方案1】:
基于Tim's second edit 和this page,您可以这样做:
@echo off
if "%1"=="loop" (
for /l %%f in (1,1,1000000) do (
echo %%f
if exist %%f exit
)
goto :eof
)
cmd /v:on /q /d /c "%0 loop"
echo done
This page 建议一种在循环中使用 goto 的方法,看起来它确实有效,但在大循环中需要一些时间。所以在内部,它在执行 goto 之前完成了循环。
【讨论】:
请勿在批处理文件中使用exit
,除非您确实想关闭当前会话。如果您从命令提示符运行批处理,那就不好了。
批处理文件作为一个整体可以在命令提示符下完美运行。这就是为什么我使用cmd /c
来调用退出的部分。
啊,没有注意到那里的cmd
调用。不过,感觉很脏。
@joey exit /b 将只退出批处理文件而不是 cmd 窗口。所以我想 exit /b 或 goto 很好【参考方案2】:
您可以简单地使用echo on
,您会看到goto :eof
甚至exit /b
无法按预期工作。
循环内的代码不再执行,但循环将所有数字展开到末尾。 这就是它如此缓慢的原因。
退出 FOR /L 循环的唯一方法似乎是 exit
的变体,就像 Wimmel 的示例一样,但这不是很快,对于访问循环中的任何结果也不是很有用。
这显示了 10 个扩展,但没有一个会被执行
echo on
for /l %%n in (1,1,10) do (
goto :eof
echo %%n
)
【讨论】:
这看起来像是问题的正确答案。 goto 具有预期的最终结果,但脚本解释器以意想不到的方式效率低下。【参考方案3】:我的回答
使用嵌套的for
循环为for /l
循环提供断点。
for %%a in (0 1 2 3 4 5 6 7 8 9) do (
for %%b in (0 1 2 3 4 5 6 7 8 9) do (
for /l %%c in (1,1,10) do (
if not exist %%a%%b%%c goto :continue
)
)
)
:continue
说明
必须对代码进行重大调整才能正确使用嵌套循环。例如,写入的内容将有前导零。
“常规”for
循环可以使用简单的goto
命令立即断开,而for /l
循环则不能。此代码最里面的for /l
循环不能立即中断,但每10 次迭代(如所写)后会出现一个整体断点。最里面的循环不一定是 10 次迭代——如果你选择做 100 或 1000 或 2873 (如果数学甚至对循环很重要),你只需要正确地考虑数学。
历史 我在试图弄清楚为什么某个脚本运行缓慢时发现了这个问题。事实证明,我使用了具有传统循环结构的多个循环:
set cnt=1
:loop
if "%somecriteria%"=="finished" goto :continue
rem do some things here
set /a cnt += 1
goto :loop
:continue
echo the loop ran %cnt% times
这个脚本文件有点长,它是从网络驱动器运行的。这种类型的循环文件可能被调用了 20 次,每次它会循环 50-100 次。脚本文件运行时间过长。我有一个好主意,就是尝试将其转换为 for /l
循环。需要的迭代次数未知,但小于 10000。我的第一次尝试是这样的:
setlocal enabledelayedexpansion
set cnt=1
for /l %%a in (1,1,10000) do (
if "!somecriteria!"=="finished" goto :continue
rem do some things here
set /a cnt += 1
)
:continue
echo the loop ran %cnt% times
启用echo
后,我很快发现for /l
循环仍然执行... 某事 ...而实际上 没有做任何事情。它跑得快得多,但仍然比我想象的要慢。因此,我发现了这个问题,并最终得到了上面介绍的嵌套循环的想法。
旁注
事实证明,只需确保for /l
循环没有任何输出,就可以大大加快它的速度。我能够这样做以显着提高速度:
setlocal enabledelayedexpansion
set cnt=1
@for /l %%a in (1,1,10000) do @(
if "!somecriteria!"=="finished" goto :continue
rem do some things here
set /a cnt += 1
) > nul
:continue
echo the loop ran %cnt% times
【讨论】:
【参考方案4】:如果您使用 call 而不是 goto ,则不需要单独的批处理文件来使用 exit /b 退出循环
call :loop
echo loop finished
goto :eof
:loop
FOR /L %%I IN (1,1,10) DO (
echo %%I
IF %%I==5 exit /b
)
在这种情况下,"exit /b" 将退出 'call' 并从 'call' 之后的行继续 所以输出是这样的:
1
2
3
4
5
loop finished
【讨论】:
你应该用for /L %%f in (1,1,10000000)
试试看,它不能解决问题,顺便说一句。 exit /b
和 goto :eof
完全相同,exit /b
在内部执行 goto :eof
为我工作。在我的情况下,我不能使用 goto 语句,因为我想从函数返回一个值并且 goto 移动到另一个函数,所以我使用了上面的解决方案和 Exit /b。【参考方案5】:
所以我意识到这有点过时了,但是经过多次谷歌搜索后,我找不到满意的答案,所以我想出了自己的解决方案来打破立即停止迭代的 FOR 循环,并认为我会分享的。
它要求循环在一个单独的文件中,并利用 CMD 错误处理中的一个错误,在将 DIR 的 STDOUT 重定向到 STDIN 时立即使循环文件的批处理崩溃。
MainFile.cmd
ECHO Simple test demonstrating loop breaking.
ECHO.
CMD /C %~dp0\LOOP.cmd
ECHO.
ECHO After LOOP
PAUSE
LOOP.cmd
FOR /L %%A IN (1,1,10) DO (
ECHO %%A
IF %%A EQU 3 DIR >&0 2>NUL )
)
运行时,会产生以下输出。您会注意到,当 %A = 3 时,循环的迭代和执行都会停止。
:>MainFile.cmd
:>ECHO Simple test demonstrating loop breaking.
Simple test demonstrating loop breaking.
:>ECHO.
:>CMD /C Z:\LOOP.cmd
:>FOR /L %A IN (1 1 10) DO (
ECHO %A
IF %A EQU 3 DIR 1>&0 2>NUL
)
:>(
ECHO 1
IF 1 EQU 3 DIR 1>&0 2>NUL
)
1
:>(
ECHO 2
IF 2 EQU 3 DIR 1>&0 2>NUL
)
2
:>(
ECHO 3
IF 3 EQU 3 DIR 1>&0 2>NUL
)
3
:>ECHO.
:>ECHO After LOOP
After LOOP
:>PAUSE
Press any key to continue . . .
如果您需要保留循环中的单个变量,请让循环 ECHO 变量的结果,并在 MainFile.cmd 中使用 FOR /F 循环来解析 LOOP.cmd 文件的输出。
示例(使用与上述相同的 LOOP.cmd 文件):
MainFile.cmd
@ECHO OFF
ECHO.
ECHO Simple test demonstrating loop breaking.
ECHO.
FOR /F "delims=" %%L IN ('CMD /C %~dp0\LOOP.cmd') DO SET VARIABLE=%%L
ECHO After LOOP
ECHO.
ECHO %VARIABLE%
ECHO.
PAUSE
输出:
:>MainFile.cmd
Simple test demonstrating loop breaking.
After LOOP
3
Press any key to continue . . .
如果您需要保留多个变量,您需要将它们重定向到临时文件,如下所示。
MainFile.cmd
@ECHO OFF
ECHO.
ECHO Simple test demonstrating loop breaking.
ECHO.
CMD /C %~dp0\LOOP.cmd
ECHO After LOOP
ECHO.
SET /P VARIABLE1=<%TEMP%\1
SET /P VARIABLE2=<%TEMP%\2
ECHO %VARIABLE1%
ECHO %VARIABLE2%
ECHO.
PAUSE
LOOP.cmd
@ECHO OFF
FOR /L %%A IN (1,1,10) DO (
IF %%A EQU 1 ECHO ONE >%TEMP%\1
IF %%A EQU 2 ECHO TWO >%TEMP%\2
IF %%A EQU 3 DIR >&0 2>NUL
)
输出:
:>MainFile.cmd
Simple test demonstrating loop breaking.
After LOOP
ONE
TWO
Press any key to continue . . .
我希望其他人发现这对于打破循环很有用,否则由于持续迭代而需要很长时间才能退出。
【讨论】:
支持,虽然我现在看到这个后觉得我的眼睛可能在流血 :-) 感谢发布这个! 我还没有尝试过这个(我很快就会尝试),但是由于循环在一个单独的批处理文件中,IF %%A EQU 3 goto :EOF
不是也可以代替IF %%A EQU 3 DIR >&0 2>NUL
工作吗?我的意思是goto :EOF
不会立即结束“LOOP.cmd”的执行吗?【参考方案6】:
作为jebnoted,循环的其余部分被跳过但被评估,这使得FOR
解决方案对于这个目的来说太慢了。另一种选择:
set F=1
:nextpart
if not exist "%F%" goto :EOF
echo %F%
set /a F=%F%+1
goto nextpart
在循环中使用它时,您可能需要使用delayed expansion 和call
子例程。
【讨论】:
简单得多。干得好。 这在小脚本文件中是一个不错的选择。对于慢(ish)网络上的大(ish)脚本文件,由于一遍又一遍地读取整个文件,这将很慢。即便如此,它仍然可以正常工作。【参考方案7】:假设 OP 正在使用 cmd.exe 调用批处理文件,要正确跳出 for 循环,只需转到标签;
改变这个:
For /L %%f In (1,1,1000000) Do If Not Exist %%f Goto :EOF
到这里:
For /L %%f In (1,1,1000000) Do If Not Exist %%f Goto:fileError
.. do something
.. then exit or do somethign else
:fileError
GOTO:EOF
更好的是,添加一些错误报告:
set filename=
For /L %%f In (1,1,1000000) Do(
set filename=%%f
If Not Exist %%f set tempGoto:fileError
)
.. do something
.. then exit or do somethign else
:fileError
echo file does not exist '%filename%'
GOTO:EOF
我发现这是一个关于鲜为人知的 cmd.exe/DOS 批处理文件功能和技巧的有用网站:https://www.dostips.com/
【讨论】:
DOS 中没有goto :eof
、for /l
或()
之类的代码块。这些是 Windows cmd 功能。它们是完全不同的环境
@phuclv 我指的是这个ss64.com/nt/for_l.html 和ss64.com/nt/goto.html。您能否详细说明“Windows cmd 功能”的含义,因为 OP 正在询问由 cmd、exe 运行的 BAT 文件。
您说的是“DOS 中的 for 循环”,而 OP 没有使用 DOS,上面的命令也无法在 DOS 中运行
@phuclv 也不知道为什么你认为 DOS 中没有代码块。我有许多带有功能的 DOS 批处理文件,也带有代码块。请参阅***.com/a/4983https://www.dostips.com/…,更完整的参考请参阅
OP 使用精确的 DOS 语言,并且还标记为批处理文件,定义为“批处理文件是包含一系列命令的文本文件,这些命令由 MS-DOS 上的命令解释器执行, IBM OS/2 或 Microsoft Windows 系统。”在我看来,这就像一个 DOS 批处理文件问题。所有其他答案也都作为 DOS 批处理答案回答。【参考方案8】:
对此进行了一些研究,您似乎正在从 1 循环到 2147483647,增量为 1。
(1, 1, 2147483647):第一个数字是开始数字,下一个数字是步骤,最后一个数字是结束数字。
编辑添加
无论任何测试条件如何,循环似乎都会运行完成。我测试过
FOR /L %%F IN (1, 1, 5) DO SET %%F=6
而且它跑得很快。
二次编辑
由于这是批处理文件中唯一的一行,您可以尝试 EXIT 命令:
FOR /L %%F IN (1, 1, 2147483647) DO @IF NOT EXIST %%F EXIT
不过,这也会关闭 DOS 提示窗口。
【讨论】:
是的,但Goto :EOF
早晚不应该跳出循环吗?
取决于是否满足 IF NOT EXIST %%F 条件。
我很确定我的文件夹中没有 任何 名称为 1
、2
等的文件...如果你认为是因为这个。 (顺便说一句,我更改了数字,所以人们不认为这是问题所在。)
我同意它应该(我已经尝试过),但它似乎没有通过 NOT EXIST 检查。我不确定为什么会这样。
它实际上失败了Goto
,但我不知道为什么。 (如果您将 Goto
重命名为 Foo
,它会抱怨。)以上是关于退出批处理文件中的 FOR 循环?的主要内容,如果未能解决你的问题,请参考以下文章
为啥即使使用 setlocal enabledelayedexpansion 也无法访问 for 循环(批处理文件)中的局部变量? [复制]
即使在设置 EnableDelayedExpansion [重复] 之后,也无法在批处理文件中的 for 循环内设置变量值