在批处理文件中跳出嵌套的for循环

9

在遇到这个问题两次后,我想把它发布在这里,看看是否有人知道如何解决它。

我似乎无法使用goto跳出嵌套循环,因为当它跳出内部循环时,括号似乎不匹配,因为它从未到达内部的闭合括号。

我已经将其简化为一个非常简单的示例。

for %%a in (1,2,3) do (
for %%b in (4,5,6) do (
echo Breaking
goto :BREAK
)
:BREAK
)

这会导致错误。
) was unexpected at this time.

我原以为添加额外的括号可能会解决问题,但是除非我知道何时会发生错误,否则它是无济于事的。如果是条件性中断,那就是同样的问题。

即使使用if和else,是否有轻松的替代方法可以从内部循环中跳出并返回到外部循环,即使它是条件性中断?

6个回答

15

通过将内部循环放置在标签中实现跳出。

for %%a in (1, 2, 3) DO (

   call :innerloop 
)
:innerloop
for %%b in (4, 5, 6) DO (
  if %%b==<something> (
    echo    break
    goto :break
  )

)
:break

提醒:如果您需要为Bash重新编写脚本,则使用GOTO不具有可移植性。如果您严重依赖GOTO,那么这将是一个重构噩梦。 - kayleeFrye_onDeck

6

您也可以在FOR循环中使用控制变量并在break后省略其余迭代:

@echo off
for %%a in (1,2,3) do (
   echo Top-level loop: %%a
   set break=
   for %%b in (4,5,6) do if not defined break (
      echo Nested loop: %%a-%%b
      if %%b == 4 (
         echo Breaking
         set break=yes
      )
   )
)

这种方法保留了嵌套的for循环体的代码,并允许在for循环体内多个位置轻松设置断点。
安东尼奥

+1 我真的很喜欢这个,因为它可以保持嵌套结构的稳定。 - Bali C
嵌套不太好,需要一个较大的循环计数。 - JasonXA

0

Antonio的提议非常好。我在解析文件列表中的一些CSV或HTML文件的示例中使用了它(嵌套的for循环)。第一个循环解析文件列表,第二个循环解析每个文件的内容。分隔符可以是;或/。

@dir /O /B j:\temp\*.txt > j:\temp\filelist.doc
@for /F "tokens=1" %%a in (j:\temp\filelist.doc) DO (
 @echo j:\temp\%%a
 @set ef=
 @for /F "delims=;/ tokens=1,2,3,4,5,6,7*" %%b in (j:\temp\%%a) DO @if not defined ef (
  @if %%e == CNAME (
   @echo Do something here
   @set ef=yes
  )
 )
)

1
不要在所有行上抑制回显,只需使用头部@echo off。 - JasonXA

0

欢迎来到DOS BAT及其固有的故障。最初,DOS BAT根本不支持嵌套循环;它们仍然不能正常工作。为了避免所有的故障、怪异和不一致性,请改用Microsoft PowerShell。

您可以通过重构脚本以使用由“call”语句调用的内部子例程来避免嵌套循环。使每个“for”执行一个调用子例程的单个语句,而不是在内联块中执行操作。

一个密切相关的问题是,即使是顶层循环的退出似乎经常无法正常工作。这是因为“(…)”方便构造几年后被添加到BAT语言中,并且与语言的其他部分不兼容。特别是,“(…)”中的所有内容在第一次进入块时就被替换为一次性。像“if %NOMORE%==yes…”这样的结构会出现问题,因为只有在进入块时才会替换%NOMORE%的值,而不是在每次执行时都会替换,这与您预期的不同。因此,如果该值稍后被内部或外部循环中的任何内容更改,则“if”测试将无法看到新值。

解决这个问题的一种方法是通过笨拙地重构BAT文件,使用大量的“goto”使其不使用“( ... )”,或者使用大量调用本来不需要的子程序,或两者兼而有之 - 这就是BAT wallahs在语法上引入块的便利性(以及伴随而来的奇怪行为)之前多年所做的。更好的方法是使用“延迟扩展”(即“如果!NOMORE!== yes ...”而不是“如果%NOMORE%== yes ...”)。[您的Windows版本必须支持延迟扩展,并且必须在setlocal语句中启用。]


由于新信息和对评论的回应,文本已经进行了大幅编辑。 (有关更多信息,请参阅第一个评论。)


1
你错过了两个重要的点:Goto会打破任何循环/代码块(如果它们被嵌套,则打破所有循环/代码块),并且标签不支持在代码块中,这意味着行为是“未定义”的(但是众所周知)。你可以使用延迟扩展而不是“笨拙地重构”。 - Stephan

0

DEFINED 可以因多种原因而变得恶劣。我不建议使用它,除非你确切地知道它的工作方式和所有潜在用例。

最好避免在变量名中使用任何分隔符字符(空格、逗号等),例如 IF DEFINED _variable 如果变量名包含分隔符字符,则通常会失败。

一个更安全的答案也不使用 EXITGOTO

@echo off

SETLOCAL ENABLEDELAYEDEXPANSION

Set time_to_exit=false

FOR /L %%O IN (1,1,3) DO (
    echo [  Outer  ] loop iteration %%O
    FOR /L %%I IN (1,1,3) DO (
        IF NOT "!time_to_exit!"=="true" (
            echo ]Inner[ loop iteration %%I
            IF "%%O"=="2" Set time_to_exit=true
        )
    )
)

输出:

[  Outer  ] loop iteration 1
]Inner[ loop iteration 1
]Inner[ loop iteration 2
]Inner[ loop iteration 3
[  Outer  ] loop iteration 2
]Inner[ loop iteration 1
[  Outer  ] loop iteration 3

-1

您能提供更多关于您需求的信息吗?同时,如果以下内容符合您的要求,您可以尝试一下。如果您需要更多帮助,请随时告诉我。

@echo off
for %%a in (1,2,3) do ( 
for %%b in (4,5,6) do (
echo Breaking
goto Break 
)
)
:Break

那会跳出两个循环,我只想跳出内部的那一个 :) - Bali C
也许你可以将内部循环放在一个标签中,并从外部循环调用该标签。 - Gaurav Kolarkar_InfoCepts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接