36. for 循环 – 循环遍历值列表


for 循环允许基于迭代变量重复执行命令序列。 Bash 支持两种 for 循环,“值列表” 和 “传统的” 类似 C 的方法,我们将在后面的部分中讨论。

“值列表” 语法:

for varname in list
do
    commands ##Body of the loop
done

  • forindodone是关键字
  • list 包含项目列表,该列表可以位于语句中,也可以从包含多个以空格分隔的单词的变量中获取。 如果for语句中缺少 list,那么 bash 将使用传递到 shell 中的位置参数。
  • varname 是任意 Bash 变量名。

在这种形式中,bash 对列表中的每个项目执行一次主体中包含的命令(在dodone之间)。 如果列表包含 5 个项目,则for循环将总共执行 5 次。 每次执行时,列表中的当前项目将在主体执行之前存储在变量 varname 中。 因此,这个 varname 可以在循环体中处理。

以下几个示例展示了如何使用 bashfor循环 “值列表” 方法。

在以下示例中,值列表(Mon, Tue, Wed, Thu 和 Fri)直接在关键in之后给出。

$ cat for1.sh
i=1
for day in Mon Tue Wed Thu Fri
do
    echo "Weekday $((i++)) : $day"
done

$ ./for1.sh
Weekday 1 : Mon
Weekday 2 : Tue
Weekday 3 : Wed
Weekday 4 : Thu
Weekday 5 : Fri

值列表不应以逗号分隔(错误写法:Mon, Tue, Wed, Thu, Fri)。 逗号将被视为值的一部分,即 bash 将使用 "Mon," 而不是 "Mon" 作为值,如下所示。

$ cat for1-wrong1.sh
i=1
for day in Mon, Tue, Wed, Thu, Fri
do
    echo "Weekday $((i++)) : $day"
done

$ ./for1-wrong1.sh
Weekday 1 : Mon,
Weekday 2 : Tue,
Weekday 3 : Wed,
Weekday 4 : Thu,
Weekday 5 : Fri

值列表不应包含在引号中。 (错误写法:"Mon Tue Wed Thu Fri")。 如果用双引号引起来,它将被视为单个值(而不是 5 个不同的值),如下所示。

$ cat for1-wrong2.sh
i=1
for day in "Mon Tue Wed Thu Fri"
do
    echo "Weekday $((i++)) : $day"
done

$ ./for1-wrong2.sh
Weekday 1 : Mon Tue Wed Thu Fri

您可以将值存储在变量中,并在 for 循环中的in关键字后面使用该变量,而不是直接在 for 循环中提供值,如以下示例所示。

$ cat for2.sh
i=1
weekdays="Mon Tue Wed Thu Fri"
for day in $weekdays
do
    echo "Weekday $((i++)) : $day"
done

$ ./for2.sh
Weekday 1 : Mon
Weekday 2 : Tue
Weekday 3 : Wed
Weekday 4 : Thu
Weekday 5 : Fri

小心:作为最佳实践,在引用 bash 变量时应始终引用它们,特殊情况除外。 这里是特殊情况之一。 如果在此 for 循环中双引号变量,则值列表将被视为单个值。 很多人都掉进了这个陷阱! 请小心,不要在 for 循环中对变量使用双引号。
$ cat for2-wrong.sh
i=1
weekdays="Mon Tue Wed Thu Fri"
for day in "$weekdays"
do
    echo "Weekday $((i++)) : $day"
done

$ ./for2-wrong.sh
Weekday 1 : Mon Tue Wed Thu Fri

如果您没有在 bash for 循环中指定关键字in后跟值列表,它将使用传递给 shell 脚本的位置参数:

$ cat for3.sh
i=1
for day
do
    echo "Weekday $((i++)) : $day"
done

$ ./for3.sh Mon Tue Wed Thu Fri
Weekday 1 : Mon
Weekday 2 : Tue
Weekday 3 : Wed
Weekday 4 : Thu
Weekday 5 : Fri

警告

使用此方法时要小心。 指定 for 循环时不应包含关键字in,因为 bash 会看到一个空列表并且永远不会执行循环。 以下是一个错误例子:

$ cat for3-wrong.sh
i=1
for day in
do
    echo "Weekday $((i++)) : $day"
done
$ ./for3-wrong.sh Mon Tue Wed Thu Fri

您可以使用任何 UNIX/Linux 命令的输出作为 for 循环的值列表,方法是将命令括在反引号 ` `中,如下例所示。

列出/etc/passwd文件中的用户名:

$ cat for4.sh
i=1
for username in `awk -F: '{print $1}' /etc/passwd`
do
    echo "Username $((i++)) : $username"
done

$ ./for4.sh
Username 1 : ramesh
Username 2 : john
Username 3 : preeti
Username 4 : jason
..

您可以使用 for 循环循环遍历特定目录下的文件和目录。 只需cd到该目录,并在 for 循环中给出* ,如下所示。

循环遍历主目录下的所有文件和目录:

$ cat for5.sh
i=1
cd ~
for item in *
do
    echo "Item $((i++)) : $item"
done

$ ./for5.sh
Item 1 : positional-parameters.sh
Item 2 : backup.sh
Item 3 : emp-report.awk
Item 4 : item-list.sed
Item 5 : employee.db

bash for 循环中*的用法类似于我们在 Linux 命令行中使用ls命令(以及其他几个命令)的文件通配符。

例如,以下将显示您的主目录下的所有文件和目录。 这是上面for5.sh示例中使用的概念。

cd ~
ls *

下面将显示/etc目录下所有以 a、b、c 或 d 开头的 *.conf 文件。

$ ls -1 /etc/[abcd]*.conf
/etc/asound.conf
/etc/autofs_ldap_auth.conf
/etc/cas.conf
/etc/cgconfig.conf
/etc/cgrules.conf
/etc/dracut.conf

用于ls命令的相同参数可以在 bash for 循环中使用,如下面的示例所示。

$ cat for5-1.sh
i=1
for file in /etc/[abcd]*.conf
do
    echo "File $((i++)) : $file"
done

$ ./for5-1.sh
File 1 : /etc/asound.conf
File 2 : /etc/autofs_ldap_auth.conf
File 3 : /etc/cas.conf
File 4 : /etc/cgconfig.conf
File 5 : /etc/cgrules.conf
File 6 : /etc/dracut.conf

您可以使用break命令跳出 for 循环,如下所示。

$ cat for6.sh
i=1
for day in Mon Tue Wed Thu Fri
do
    echo "Weekday $((i++)) : $day"
    if [ $i -eq 3 ]; then
        break;
    fi
done

$ ./for6.sh
Weekday 1 : Mon
Weekday 2 : Tue

在某些情况下,您可以忽略 for 循环体中的其余命令,并使用如下所示的continue命令再次从顶部继续循环(使用列表中的下一个值)。

以下示例将"(WEEKEND)"添加到 Sat 和 Sun,并将"(weekday)"添加到其余日期。

$ cat for7.sh
i=1

for day in Mon Tue Wed Thu Fri Sat Sun
do
    echo -n "Day $((i++)) : $day"
    if [ $i -eq 7 -o $i -eq 8 ]; then
        echo " (WEEKEND)"
        continue;
    fi
    echo " (weekday)"
done

$ ./for7.sh
Day 1 : Mon (weekday)
Day 2 : Tue (weekday)
Day 3 : Wed (weekday)
Day 4 : Thu (weekday)
Day 5 : Fri (weekday)
Day 6 : Sat (WEEKEND)
Day 7 : Sun (WEEKEND)

您可能已经注意到,包含第二个echoelse语句也会起作用。 通常有多种方法可以完成同一件事。

您可以通过在in关键字后面使用大括号扩展来循环遍历 for 循环中的数字范围。

以下示例循环遍历值 1 到 10。

$ cat for8.sh
for num in {1..10}
do
    echo "Number: $num"
done

$ ./for8.sh
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
...

以下示例循环访问一组数字,但每次循环变量都会增加 2,而不是 1。 循环将执行五次,因为之后循环变量将超出范围。

从 1 到 10 按 2 循环,即显示从 1 到 10 的奇数:

$ cat for9.sh
for num in {1..10..2}
do
    echo "Number: $num"
done

$ ./for9.sh
Number: 1
Number: 3
Number: 5
Number: 7
Number: 9