我如何在Bash中编写'for'循环?

143

我正在寻找像这样的基本循环:

for(int i = 0; i < MAX; i++) {
  doSomething(i);
}

但对于Bash来说。


2
可能是重复的问题:如何在Bash中迭代由变量定义的数字范围? - Amir Ali Akbari
9个回答

166

24
值得一提的是,这里指定的范围是包括在内的。也就是说,您将在控制台上看到整个范围(从1到10)的输出。 - Jamie

91
for ((i = 0 ; i < max ; i++ )); do echo "$i"; done

如果我们有一个变量,例如ArrayLength=${#array[@]},我们该如何在这里使用它来代替max - Kasun Siyambalapitiya
我更喜欢这个解决方案,因为它不使用 seq,所以不会生成一个包含所有数字序列的奇怪字符串,而是可以高效地循环整数。 - Valerio Bozz

50

Bash中的for循环由一个变量(迭代器)和一个单词列表组成,迭代器将会在列表中遍历。

因此,如果您有一个有限的单词列表,只需按照以下语法将它们放入:

for w in word1 word2 word3
do
  doSomething($w)
done

您可能想要迭代一些数字,这时可以使用 seq 命令为您生成数字列表:(例如从1到100)

seq 1 100

并在for循环中使用它:

for n in $(seq 1 100)
do
  doSomething($n)
done

注意 $(...) 的语法。这是 Bash 的一种行为,它允许您将一个命令的输出(在我们的例子中是从 seq)传递给另一个命令(for)。 当你需要迭代某个路径下的所有目录时,这非常有用,例如:
for d in $(find $somepath -type d)
do
  doSomething($d)
done

生成列表的可能性是无限的。

2
不错的答案,但您可能还想包括 for ((i=0; i<MAX; i++)); do doSomething($i); done 这种变体。我认为这通常比 for i in $(seq 0 MAX) 更受欢迎,因为后者将在实际执行循环之前首先生成从0到MAX的所有数字。 - mweerden

45

Bash 3.0+ 可以使用这种语法:

for i in {1..10} ; do ... ; done

...这种方法避免了生成序列时调用外部程序(如seq 1 10)的需求。

当然,这种方法也存在与for(())解决方案相同的问题,即与Bash相关,甚至与特定版本相关(如果这对您很重要的话)。


17

尝试使用Bash内置帮助:

help for

输出

for: for NAME [in WORDS ... ;] do COMMANDS; done
    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.

for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
    Equivalent to
        (( EXP1 ))
        while (( EXP2 )); do
            COMMANDS
            (( EXP3 ))
        done
    EXP1, EXP2, and EXP3 are arithmetic expressions.  If any expression is
    omitted, it behaves as if it evaluates to 1.

5
#! /bin/bash

function do_something {
   echo value=${1}
}

MAX=4
for (( i=0; i<MAX; i++ )) ; {
   do_something ${i}
}

这是一个例子,即使在旧的shell中也可以工作,同时对于大计数仍然很有效:

Z=$(date) awk 'BEGIN { for ( i=0; i<4; i++ ) { print i,"hello",ENVIRON["Z"]; } }'

但是在 awk 中做有用的事情是需要一些运气的:如何在 awk 脚本中使用 shell 变量?


3
以前我没有注意到,但在之前的答案中显示了类似的语法。这里独特的地方是使用花括号而不是典型的 do/done 对。 - Brent Bradburn

4

我通常喜欢使用标准的for循环的一个小变种。我经常用它来在一系列远程主机上运行命令。我利用Bash的花括号扩展功能创建非数字for循环。

例如:

我想在前端主机1-5和后端主机1-3上运行uptime命令:

% for host in {frontend{1..5},backend{1..3}}.mycompany.com
    do ssh $host "echo -n $host; uptime"
  done

通常我会将这个命令以单行形式运行,每行末尾加上分号,而不是上面更易读的版本。重要的用法考虑是花括号允许您指定多个值插入到字符串中(例如pre{foo,bar}post得到prefoopost,prebarpost),并且可以使用双点号进行计数/序列(您可以使用a..z等)。然而,双点号语法是Bash 3.0的新功能;早期版本将不支持此功能。


关于这个问题,如果$host变量不小心为空,它仍然会触发ssh。那么,我该如何避免这种情况呢?在我的情况下,我正在做一些不同的事情,所以你的答案会对我有所帮助。我正在尝试检查Gmail是否有新邮件,如果有,发送包含发件人和主题的短信。如果您想阅读我的问题,请查看标记为“连接两个Bash命令”的问题。 - Volomike
我不相信你会得到一个空的$host变量。这是因为上面的示例使用了花括号扩展。for循环明确将$host变量设置为以下值:frontend1.mycompany.com frontend2.mycompany.com . . backend1.mycompany.com但是,如果您发现它可能具有空值,我很想知道。如果这是一个问题,您可以在条件测试内执行ssh。 - terson

1

如果你只对Bash感兴趣,上面介绍的"for(( ... ))"解决方案是最好的选择,但如果你想要一个符合POSIX SH标准并且能在所有Unix系统上运行的解决方案,你就必须使用"expr"和"while",因为"(())"、"seq"或"i=i+1"在不同的shell中并不是那么具有可移植性。


0

我经常使用这种方法来处理文件...

for files in *.log; do echo "对文件进行操作: $files"; echo "对文件进行更多操作: $files"; done;

如果你对处理文件列表感兴趣,可以研究一下-execdir选项。


我认为你指的是find命令的-execdir选项。 - Dennis Williamson

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