如何在批处理中循环遍历数组?

55

我创建了这样一个数组:

set sources[0]="\\sources\folder1\"
set sources[1]="\\sources\folder2\"
set sources[2]="\\sources\folder3\"
set sources[3]="\\sources\folder4\"
现在我想要遍历这个数组:
for %%s in (%sources%) do echo %%s

它不起作用!似乎脚本没有进入循环。为什么?我该如何迭代?

6个回答

57

另一个替代方案是使用“defined”和循环,不需要延迟扩展:

set Arr[0]=apple
set Arr[1]=banana
set Arr[2]=cherry
set Arr[3]=donut

set "x=0"

:SymLoop
if defined Arr[%x%] (
    call echo %%Arr[%x%]%%
    set /a "x+=1"
    GOTO :SymLoop
)

请确保使用"call echo",因为如果您没有使用延迟扩展并且使用%%而非!,echo将无法正常工作。


1
我喜欢这种方式。它有点简单。 - Farrukh Waheed
1
设计批处理文件中循环语法的人一定是个幽默的家伙。 - dzieciou

42

如果你不知道数组有多少元素(似乎是这种情况),你可以使用以下方法:

for /F "tokens=2 delims==" %%s in ('set sources[') do echo %%s

请注意,元素将按字母顺序进行处理,也就是说,如果您有超过9(或99等)个元素,则索引必须在1..9(或1..99等)的元素中留下零。


31
如果您不需要环境变量,请执行以下操作:

for %%s in ("\\sources\folder1\" "\\sources\folder2\" "\\sources\folder3\" "\\sources\folder4\") do echo %%s

12
这绝对不是有帮助的:/ 我问如何遍历数组,而你给出的答案是:“就是不要用数组!” - aurel
25
我给出这个建议只是因为有时候我会根据可能的解决方案重新考虑我的程序架构。想象一下,如果你只是为了能够迭代项目而创建了一个数组,那么在没有先前的数组的情况下进行迭代将使该数组变得不必要! - LS_ᴅᴇᴠ
请注意,in( 之间的空格很重要。这样是不行的:in("\\sources...),但如果你加上空格写成 in ("\\sources...) 就可以正常运行了。 - Dimitry K

18

这是一种方法:

@echo off
set sources[0]="\\sources\folder1\"
set sources[1]="\\sources\folder2\"
set sources[2]="\\sources\folder3\"
set sources[3]="\\sources\folder4\"

for /L %%a in (0,1,3) do call echo %%sources[%%a]%%

7
哦,你这个信仰不够坚定的人。试试看就知道了。 :) - foxidrive
1
你说得对,我道歉!另外,无论数组长度如何,拥有动态迭代会很好。 - Wim
请注意,for /L 循环需要 (0,1,<last_element_id>) 的参数,本例中的 last_element_id 为 3。 - Zac

5

为了后人留存: 我只想在@dss的优秀答案上提出轻微修改建议。

当前结构下,在循环内将Arr的值分配给临时变量时,DEFINED检查的方式会导致意外输出:

例如:

@echo off
set Arr[0]=apple
set Arr[1]=banana
set Arr[2]=cherry
set Arr[3]=donut

set "x=0"

:SymLoop
if defined Arr[%x%] (
    call set VAL=%%Arr[%x%]%%
    echo %VAL%
    REM do stuff with VAL
    set /a "x+=1"
    GOTO :SymLoop
)

这实际上会产生以下错误的输出。
donut
apple
banana
cherry

最后一个元素首先被打印。 为了解决这个问题,更简单的方法是倒转DEFINED检查,让它在我们完成数组后跳过循环而不是执行它。像这样:
@echo off
set Arr[0]=apple
set Arr[1]=banana
set Arr[2]=cherry
set Arr[3]=donut

set "x=0"

:SymLoop
if not defined Arr[%x%] goto :endLoop
call set VAL=echo %%Arr[%x%]%%
echo %VAL%
REM do your stuff VAL
SET /a "x+=1"
GOTO :SymLoop

:endLoop
echo "Done"

无论您在SymLoop内部执行什么操作,它始终会生成所需的正确输出。
apple
banana
cherry
donut
"Done"

1
你第一个例子中的问题很简单,只需要将 echo %var% 改为 call echo %%var%% 即可按预期工作。或者更好的方法是使用延迟扩展。 - jeb
@jeb 这是问题的关键,循环内部的代码表现不像在正常批处理脚本中所期望的那样。您需要以不同的方式调用事物。这将会是长期维护上的痛点..最好确保它按用户期望工作。 - Sverrir Sigmundarson
1
它在批处理脚本中的行为完全符合预期!重要的是要了解批处理的工作原理,而不是绕过你不理解的结构。而且你无法绕过所有括号块。 - jeb

5
我这样使用,重要的是变量只有一个长度,例如%%a,而不是像%%repo:这样的多个长度。
for %%r in ("https://github.com/patrikx3/gitlist" "https://github.com/patrikx3/gitter" "https://github.com/patrikx3/corifeus" "https://github.com/patrikx3/corifeus-builder" "https://github.com/patrikx3/gitlist-workspace" "https://github.com/patrikx3/onenote" "https://github.com/patrikx3/resume-web") do (
   echo %%r
   git clone --bare %%r
)

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