流程控制:for 循环

    实现一个 for 循环,很自然的,要用 for 命令。在现代版的 bash 中,有两种可用的 for 循环格式。

    for 命令语法是:

    这里的 variable 是一个变量的名字,这个变量在循环执行期间会增加,words 是一个可选的条目列表,其值会按顺序赋值给 variable,commands 是在每次循环迭代中要执行的命令。

    在命令行中 for 命令是很有用的。我们可以很容易的说明它是如何工作的:

    1. A
    2. B
    3. C
    4. D

    在这个例子中,for 循环有一个四个单词的列表:“A”、“B”、“C”和 “D”。由于这四个单词的列表,for 循环会执行四次。每次循环执行的时候,就会有一个单词赋值给变量 i。在循环体内,我们有一个 echo 命令会显示 i 变量的值,来演示赋值结果。正如 while 和 until 循环,done 关键字会关闭循环。

    for 命令真正强大的功能是我们可以通过许多有趣的方式创建 words 列表。例如,通过花括号展开:

    1. [me@linuxbox ~]$ for i in {A..D}; do echo $i; done
    2. A
    3. B
    4. C
    5. D

    或者路径名展开:

    1. [me@linuxbox ~]$ for i in distros*.txt; do echo $i; done
    2. distros-by-date.txt
    3. distros-dates.txt
    4. distros-key-names.txt
    5. distros-key-vernums.txt
    6. distros-names.txt
    7. distros.txt
    8. distros-vernums.txt
    9. distros-versions.txt

    在这个示例中,我们要在一个文件中查找最长的字符串。当在命令行中给出一个或多个文件名的时候,该程序会使用 strings 程序(其包含在 GNU binutils 包中),为每一个文件产生一个可读的文本格式的 “words” 列表。然后这个 for 循环依次处理每个单词,判断当前这个单词是否为目前为止找到的最长的一个。当循环结束的时候,显示出最长的单词。

    如果省略掉 for 命令的可选项 words 部分,for 命令会默认处理位置参数。我们将修改 longest-word 脚本,来使用这种方式:

    1. #!/bin/bash
    2. # longest-word2 : find longest string in a file
    3. for i; do
    4. if [[ -r $i ]]; then
    5. max_word=
    6. max_len=0
    7. len=$(echo $j | wc -c)
    8. if (( len > max_len )); then
    9. max_len=$len
    10. fi
    11. done
    12. echo "$i: '$max_word' ($max_len characters)"
    13. fi
    14. done

    正如我们所看到的,我们已经更改了最外围的循环,用 for 循环来代替 while 循环。通过省略 for 命令的 words 列表,用位置参数替而代之。在循环体内,之前的变量 i 已经改为变量 j。同时 shift 命令也被淘汰掉了。

    最新版本的 bash 已经添加了第二种格式的 for 命令语法,该语法相似于 C 语言中的 for 语法格式。其它许多编程语言也支持这种格式:

    1. for (( expression1; expression2; expression3 )); do
    2. commands
    3. done
    1. (( expression1 ))
    2. while (( expression2 )); do
    3. commands
    4. (( expression3 ))
    5. done

    expression1 用来初始化循环条件,expression2 用来决定循环结束的时间,还有在每次循环迭代的末尾会执行 expression3。

    这里是一个典型应用:

    脚本执行之后,产生如下输出:

    1. [me@linuxbox ~]$ simple_counter
    2. 0
    3. 1
    4. 2
    5. 3
    6. 4

    在这个示例中,expression1 初始化变量 i 的值为0,expression2 允许循环继续执行只要变量 i 的值小于5,还有每次循环迭代时,expression3 会把变量 i 的值加1。

    C 语言格式的 for 循环对于需要一个数字序列的情况是很有用处的。我们将在接下来的两章中看到几个这样的应用实例。

    学习了 for 命令的知识,现在我们将对我们的 sys_info_page 脚本做最后的改进。目前,这个 report_home_space 函数看起来像这样:

    1. report_home_space () {
    2. if [[ $(id -u) -eq 0 ]]; then
    3. cat <<- _EOF_
    4. <H2>Home Space Utilization (All Users)</H2>
    5. <PRE>$(du -sh /home/*)</PRE>
    6. _EOF_
    7. else
    8. <H2>Home Space Utilization ($USER)</H2>
    9. _EOF_
    10. fi
    11. return
    12. }

    下一步,我们将重写它,以便提供每个用户家目录的更详尽信息,并且包含用户家目录中文件和目录的总个数:

    1. report_home_space () {
    2. local format="%8s%10s%10s\n"
    3. local i dir_list total_files total_dirs total_size user_name
    4. if [[ $(id -u) -eq 0 ]]; then
    5. dir_list=/home/*
    6. user_name="All Users"
    7. else
    8. dir_list=$HOME
    9. user_name=$USER
    10. fi
    11. echo "<H2>Home Space Utilization ($user_name)</H2>"
    12. for i in $dir_list; do
    13. total_files=$(find $i -type f | wc -l)
    14. total_dirs=$(find $i -type d | wc -l)
    15. total_size=$(du -sh $i | cut -f 1)
    16. echo "<H3>$i</H3>"
    17. echo "<PRE>"
    18. printf "$format" "Dirs" "Files" "Size"
    19. printf "$format" "----" "-----" "----"
    20. printf "$format" $total_dirs $total_files $total_size
    21. echo "</PRE>"
    22. done
    23. return
    24. }
    • 《Bash 参考手册》描述了循环复合命令,包括了 for 循环: