MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Bash循环语句while与for深入

2023-01-033.5k 阅读

1. Bash 循环语句概述

在 Bash 脚本编程中,循环语句是非常重要的控制结构,它允许我们重复执行一段代码块,直到满足特定的条件。Bash 提供了多种循环语句,其中 whilefor 是最常用的两种。while 循环基于条件判断来决定是否继续循环,而 for 循环则常用于遍历列表或执行固定次数的循环。熟练掌握这两种循环语句对于编写高效、灵活的 Bash 脚本至关重要。

2. while 循环深入

2.1 while 循环基本语法

while 循环的基本语法结构如下:

while condition
do
    commands
done

在这个结构中,condition 是一个条件表达式,commands 是要重复执行的命令块。只要 condition 的返回值为真(通常以命令执行成功,即返回状态码为 0 表示真),commands 就会被不断执行。

例如,我们可以编写一个简单的 while 循环来打印数字 1 到 5:

#!/bin/bash
count=1
while [ $count -le 5 ]
do
    echo $count
    count=$((count + 1))
done

在这个例子中,[ $count -le 5 ] 是条件表达式,它判断 count 的值是否小于或等于 5。只要这个条件为真,就会执行 echo $count 打印当前的 count 值,然后 count 自增 1。

2.2 while 循环的条件判断

while 循环的条件判断可以是多种形式,除了上述的使用 [] 进行数值比较外,还可以:

  • 文件测试:例如判断文件是否存在。
while [ -f file.txt ]
do
    echo "文件存在,执行相关操作"
    # 执行文件操作
    sleep 1
done

这里 -f 是文件测试操作符,判断 file.txt 是否为普通文件。只要文件存在,循环就会持续执行。

  • 命令执行结果判断:使用命令的返回状态码来决定是否继续循环。
while ping -c 1 192.168.1.1 &> /dev/null
do
    echo "主机 192.168.1.1 可达"
    sleep 5
done

在这个例子中,ping -c 1 192.168.1.1 &> /dev/null 尝试向 192.168.1.1 发送一个 ICMP 数据包,并将输出重定向到 /dev/null 以避免显示不必要的信息。如果 ping 命令成功(即主机可达),返回状态码为 0,循环会持续执行。

2.3 while 循环中的无限循环

有时候我们需要编写一个无限循环,这在某些服务器守护进程或监控脚本中很常见。在 while 循环中实现无限循环可以使用以下两种常见方式:

  • 使用永远为真的条件
while true
do
    echo "这是一个无限循环"
    sleep 1
done

这里 true 是一个永远返回真的命令,所以循环会一直执行下去,除非手动终止脚本(例如使用 Ctrl+C)。

  • 省略条件
while :
do
    echo "这也是一个无限循环"
    sleep 1
done

: 是一个空命令,它总是返回真,同样会导致无限循环。

2.4 while 循环中的 breakcontinue

  • break 语句break 语句用于立即终止当前循环,跳出循环体。
count=1
while [ $count -le 10 ]
do
    if [ $count -eq 5 ]
    then
        break
    fi
    echo $count
    count=$((count + 1))
done

在这个例子中,当 count 的值等于 5 时,break 语句被执行,循环立即终止,不会再打印 6 到 10 的数字。

  • continue 语句continue 语句用于跳过当前循环中剩余的命令,直接进入下一次循环。
count=1
while [ $count -le 10 ]
do
    if [ $count -eq 5 ]
    then
        count=$((count + 1))
        continue
    fi
    echo $count
    count=$((count + 1))
done

这里当 count 等于 5 时,continue 语句使循环跳过 echo $count 这一行,直接进入下一次循环,所以不会打印 5。

2.5 while 循环读取输入

while 循环常与 read 命令结合,用于逐行读取输入。例如,假设我们有一个文本文件 data.txt,内容如下:

apple
banana
cherry

我们可以使用 while 循环读取文件内容并进行处理:

#!/bin/bash
while read line
do
    echo "读取到的行: $line"
done < data.txt

在这个脚本中,while read linedata.txt 文件中逐行读取内容,并将每一行赋值给变量 line,然后通过 echo 打印出来。

2.6 while 循环嵌套

while 循环可以嵌套使用,以实现更复杂的逻辑。例如,我们可以用嵌套的 while 循环来打印一个乘法表:

#!/bin/bash
i=1
while [ $i -le 9 ]
do
    j=1
    while [ $j -le 9 ]
    do
        result=$((i * j))
        echo -n "$i * $j = $result  "
        j=$((j + 1))
    done
    echo
    i=$((i + 1))
done

在这个脚本中,外层 while 循环控制行数,内层 while 循环控制每一行的列数。通过计算乘法结果并打印,最终生成一个 9x9 的乘法表。

3. for 循环深入

3.1 for 循环基本语法

for 循环在 Bash 中有两种常见的语法形式。第一种是基于列表的循环:

for variable in list
do
    commands
done

在这个结构中,variable 会依次取 list 中的每个值,然后执行 commands 命令块。例如:

for fruit in apple banana cherry
do
    echo "我喜欢的水果: $fruit"
done

这里 fruit 会依次取值为 applebananacherry,每次取值都会执行 echo 命令。

第二种语法形式是 C 风格的 for 循环:

for (( initial; condition; increment ))
do
    commands
done

其中 initial 是初始化语句,condition 是条件判断语句,increment 是每次循环结束后执行的递增或递减语句。例如:

for (( i = 1; i <= 5; i++ ))
do
    echo $i
done

在这个例子中,i 初始值为 1,每次循环判断 i 是否小于等于 5,满足条件则执行 echo $i,然后 i 自增 1。

3.2 for 循环的列表生成

  • 手动指定列表:如前面例子中的 for fruit in apple banana cherry,我们可以手动列出元素。
  • 使用通配符:通配符可以用于生成文件列表。例如,要遍历当前目录下所有的 .txt 文件:
for file in *.txt
do
    echo "处理文件: $file"
    # 执行文件处理操作
done
  • 使用序列生成{start..end} 语法可以生成一个数字序列。例如,要打印 1 到 10 的数字:
for num in {1..10}
do
    echo $num
done

还可以指定步长,如 {start..end..step}。例如,打印 1 到 10 中的奇数:

for odd in {1..10..2}
do
    echo $odd
done

3.3 for 循环中的变量作用域

for 循环中定义的变量,其作用域默认是整个脚本。例如:

for i in {1..5}
do
    echo $i
done
echo "循环结束后,i 的值为: $i"

在这个例子中,即使循环结束,i 的值仍然可以在脚本后续部分访问到。如果希望限制变量作用域,可以使用 local 关键字(在函数内部使用)。

3.4 for 循环中的 breakcontinue

while 循环类似,for 循环也支持 breakcontinue 语句。

  • break 语句:用于立即终止 for 循环。
for num in {1..10}
do
    if [ $num -eq 5 ]
    then
        break
    fi
    echo $num
done

这里当 num 等于 5 时,break 语句执行,循环终止,不会打印 6 到 10 的数字。

  • continue 语句:用于跳过当前循环中剩余的命令,进入下一次循环。
for num in {1..10}
do
    if [ $num -eq 5 ]
    then
        continue
    fi
    echo $num
done

num 等于 5 时,continue 语句使循环跳过 echo $num,直接进入下一次循环,不会打印 5。

3.5 for 循环嵌套

for 循环也可以嵌套使用。例如,我们可以用嵌套的 for 循环来打印一个三角形:

#!/bin/bash
for (( i = 1; i <= 5; i++ ))
do
    for (( j = 1; j <= i; j++ ))
    do
        echo -n "*"
    done
    echo
done

在这个脚本中,外层 for 循环控制行数,内层 for 循环控制每一行的星号数量,从而打印出一个三角形。

3.6 for 循环与数组

for 循环常与数组结合使用,用于遍历数组元素。例如:

fruits=("apple" "banana" "cherry")
for fruit in "${fruits[@]}"
do
    echo "数组中的水果: $fruit"
done

这里使用 "${fruits[@]}" 来获取数组 fruits 的所有元素,for 循环依次将每个元素赋值给 fruit 变量并进行处理。

4. whilefor 循环的比较与选择

  • 逻辑侧重点while 循环更侧重于基于条件的循环,适用于不确定循环次数,需要根据某个条件动态决定是否继续循环的场景,比如监控某个服务是否可用。而 for 循环更侧重于固定次数或基于列表的循环,例如遍历文件列表或执行固定次数的任务。
  • 代码简洁性:在处理已知次数的循环或遍历列表时,for 循环的语法通常更简洁明了。例如遍历 1 到 100 的数字,for (( i = 1; i <= 100; i++ )) 比使用 while 循环实现起来更简洁。但在基于复杂条件判断的循环中,while 循环能更好地表达逻辑。
  • 性能考量:在大多数情况下,两者的性能差异并不明显。但在一些极端情况下,如超大规模的循环,C 风格的 for 循环由于其紧凑的结构和明确的循环控制,可能在性能上略微优于 while 循环。不过这种差异在一般的脚本编程中很难察觉。

在实际编写 Bash 脚本时,应根据具体的需求和逻辑选择合适的循环语句,以提高代码的可读性和可维护性。例如,在编写一个备份脚本,需要遍历指定目录下的所有文件时,for 循环可能是更好的选择;而在编写一个监控脚本,需要持续检查某个进程是否运行时,while 循环则更为合适。

通过深入理解 whilefor 循环的各种特性、语法和应用场景,我们能够编写出更加高效、灵活和功能强大的 Bash 脚本,满足各种自动化任务和系统管理的需求。无论是简单的文件处理,还是复杂的系统监控和任务调度,这两种循环语句都是 Bash 脚本编程的重要工具。在实际应用中,不断练习和尝试不同的循环用法,将有助于我们更好地掌握 Bash 编程技巧,提升编程能力。

例如,我们可以编写一个结合 whilefor 循环的复杂脚本。假设我们有多个服务器列表存储在文件 servers.txt 中,每行一个服务器地址,我们要对每个服务器进行多次健康检查:

#!/bin/bash
while read server
do
    for (( i = 1; i <= 3; i++ ))
    do
        if ping -c 1 $server &> /dev/null
        then
            echo "第 $i 次检查,服务器 $server 可达"
        else
            echo "第 $i 次检查,服务器 $server 不可达"
        fi
    done
done < servers.txt

在这个脚本中,while 循环逐行读取 servers.txt 中的服务器地址,for 循环则对每个服务器进行 3 次健康检查,并根据 ping 命令的结果输出相应的信息。

又如,我们可以用 while 循环结合文件读取和 for 循环处理数据来统计文本文件中每个单词出现的次数。假设文本文件 text.txt 内容如下:

hello world
hello bash
bash script

我们可以编写如下脚本:

#!/bin/bash
declare -A word_count
while read line
do
    for word in $line
    do
        if [ -z "${word_count[$word]}" ]
        then
            word_count[$word]=1
        else
            word_count[$word]=$((word_count[$word] + 1))
        fi
    done
done < text.txt
for word in "${!word_count[@]}"
do
    echo "$word: ${word_count[$word]} 次"
done

在这个脚本中,while 循环逐行读取 text.txt 的内容,for 循环在每一行中拆分出单词,并使用关联数组 word_count 统计每个单词出现的次数。最后,通过另一个 for 循环遍历关联数组并输出每个单词及其出现次数。

通过这些实际的例子,我们可以更深入地理解 whilefor 循环在复杂场景下的应用和结合方式,进一步提升我们编写高效 Bash 脚本的能力。在实际项目中,根据具体需求合理选择和组合这两种循环语句,能够极大地提高脚本的质量和效率,实现各种自动化任务和系统管理目标。无论是系统管理员处理日常任务,还是开发人员进行自动化部署和测试,熟练掌握 whilefor 循环都是必不可少的技能。