Shell循环

回顾

上一章学习的结构化命令中的 if-thencase 语句,其中 if-then 语句是通过检查命令的执行结果或者表达式(文件比较、字符串比较、数值比较)的结果来控制脚本的执行流程。

在Shell脚本中,循环语句是一种重要的控制结构,用于多次执行同一段代码。Shell脚本中提供了多种循环语句,包括forwhileuntil三种。

for 循环结构

for 循环是一种针对列表中每个元素执行相同操作的循环结构。该语句会将列表中的每个元素依次赋值给变量,并执行循环体中的语句。其基本语法格式如下:

for 变量名 in 列表
do
    循环体
done

其中,变量名是一个新的变量,用于存储列表中的每个元素;列表是一个由空格分隔的参数列表。

例如,下面的代码中,for 循环遍历了一个包含 5 个元素的列表,依次输出了每个元素:

#!/bin/bash
# 遍历数字 1 到 5 并输出
for i in 1 2 3 4 5
do
    echo "[当前进度] 正在处理元素:$i"
done

echo "[完成] 循环结束,最后变量 i 的值为:$i"

以上代码会输出以下结果:

[当前进度] 正在处理元素:1
[当前进度] 正在处理元素:2
[当前进度] 正在处理元素:3
[当前进度] 正在处理元素:4
[当前进度] 正在处理元素:5
[完成] 循环结束,最后变量 i 的值为:5
  • 读取列表中的复杂值

    #!/bin/bash
    # 演示处理带单引号的复杂字符串
    for i in I don't know if this'll work
    do
        echo "[拆分] 发现单词:$i"
    done

    以上代码输出以下结果:

    [拆分] 发现单词:I
    [拆分] 发现单词:dont know if thisll
    [拆分] 发现单词:work
    • 两种方法解决列表中单引号引用的问题

      1. 使用转义字符(反斜杠 \)将单引号转义
      2. 使用双引号 "" 定义用到单引号的值
      #!/bin/bash
      # 方法 1:使用反斜杠转义
      for i in I don\'t know if this\'ll work
      do
          echo "[处理结果] 单词:$i"
      done
      
      # 方法 2:使用双引号包裹
      for i in I "don't" know if "this'll" work
      do
          echo "[处理结果] 单词:$i"
      done
    • 使用双引号引用输出多个单词

      #!/bin/bash
      # 使用双引号将带空格的字符串视为单个元素
      for i in "1 2" "3 4" 5
      do
          echo "[元素] 列表中的内容:$i"
      done
      
      echo "最后变量 i 的值为:$i"

      以上代码会输出以下结果:

      [元素] 列表中的内容:1 2
      [元素] 列表中的内容:3 4
      [元素] 列表中的内容:5
      最后变量 i 的值为:5
  • 从变量读取列表

    #!/bin/bash
    # 使用变量存储列表并动态添加元素
    list="1 2 3 4 5"
    list=$list" 6"   # 在列表末尾追加 6
    
    for i in $list
    do
        echo "[步骤] 处理数字:$i"
    done

    以上代码会输出以下结果:

    [步骤] 处理数字:1
    [步骤] 处理数字:2
    [步骤] 处理数字:3
    [步骤] 处理数字:4
    [步骤] 处理数字:5
    [步骤] 处理数字:6
  • 通过命令执行结果读取列表

    编辑以下脚本,通过反引号 `$() 运行命令并获取输出作为循环内容。

    #!/bin/bash
    
    # 遍历家目录下的所有文件并使用 file 命令检测类型
    for i in `ls ~`
    do
        echo "检查文件: $i"
        file $i
    done
  • 用通配符读取目录

    在 Shell 编程中,通配符是一种用于匹配文件名或目录名的特殊字符。通配符可以帮助你快速地匹配一组文件或目录,从而方便地对它们进行操作。

    常见的通配符包括:

    • *:匹配任意长度的任意字符,包括空字符串。

    • ?:匹配任意一个字符。

    • [...]:匹配括号内列举的任意一个字符。

    • [^...]:匹配除了括号内列举的任意一个字符以外的所有字符。

      以下是一些通配符匹配的示例:

    • *.txt:匹配所有以 .txt 结尾的文件,如 file1.txtfile2.txt 等。

    • file?.txt:匹配所有文件名为 file 开头,后面跟着任意一个字符,最后以 .txt 结尾的文件,如 file1.txtfileA.txt 等。

    • [abc].txt:匹配所有文件名为 a.txtb.txtc.txt 的文件。

    • [^abc].txt:匹配除了文件名为 a.txtb.txtc.txt 以外的所有以 .txt 结尾的文件。

      #!/bin/bash
      
      touch ~/file{1..2} >/dev/null
      mkdir ~/dir{1..3} >/dev/null
      
      for file in ~/*
      do
          if [ -d "$file" ]
          then
           echo "$file 是一个目录"
          elif [ -f "$file" ]
          then
              echo "$file 是一个文件"
          fi
      done

作业1:参考以下代码段,通过shell脚本统计/var/log目录下每个文件的行数。

#!/bin/bash

#通过命令赋值获取待统计的文件列表
file=`ls -d /var/log/*`
#使用wc命令统计文件行数
wc -l /var/log/*

while 循环结构

while 循环是一种基于条件判断的循环结构,当条件满足时就会一直执行循环体中的语句。其基本语法格式如下:

while [ 条件 ]
do
    循环体
done

其中,条件是一个可以执行的测试命令或表达式,如果返回值为0,则为真,循环继续执行;否则为假,循环结束。

例如,下面的代码中,while 循环会一直执行,直到变量 i 的值达到 5:

#!/bin/sh
i=1
# 当 i 小于或等于 5 时持续循环
while [ $i -le 5 ]
do
    echo "[当前计数] 变量 i 的值是:$i"
    i=$(( i + 1 ))  # 递增变量,也可以写成 ((i++))
done

以上代码会输出以下结果:

[当前计数] 变量 i 的值是:1
[当前计数] 变量 i 的值是:2
[当前计数] 变量 i 的值是:3
[当前计数] 变量 i 的值是:4
[当前计数] 变量 i 的值是:5

作业2:编写脚本实现以下功能:
1.提示用户输入数字
2.使用while循环计算该数字的阶乘后输出计算结果

until 循环结构

until 循环与 while 循环类似,但是只有当条件为假时才会执行循环体中的语句。其基本语法格式如下:

until [ 条件 ]
do
    循环体
done

其中,条件是一个可以执行的测试命令或表达式,如果返回值为0,则为假,循环继续执行;否则为真,循环结束。

例如,下面的代码中,until 循环会一直执行,直到变量 i 的值达到 5:

#!/bin/bash
i=1
# 直到 i 大于 5 时才停止循环
until [ $i -gt 5 ]
do
    echo "[倒计时/计数] 变量 i 目前为:$i"
    i=$(( $i + 1 ))
done

以上代码会输出以下结果:

[倒计时/计数] 变量 i 目前为:1
[倒计时/计数] 变量 i 目前为:2
[倒计时/计数] 变量 i 目前为:3
[倒计时/计数] 变量 i 目前为:4
[倒计时/计数] 变量 i 目前为:5

作业3:编写脚本实现以下功能:
1.提示用户输入数字
2.使用until循环计算该数字的平方和后输出计算结果

循环控制语句结构

在循环中,有时需要跳过某些循环或终止循环的执行,Shell脚本提供了以下三种循环控制语句:

  • break语句:用于强制退出循环,跳出循环体,执行循环后面的语句。
  • continue语句:用于跳过循环体中的一个迭代,直接进入下一个迭代。
  • return语句:用于退出函数,返回到函数调用处。

例如,下面的代码演示了如何在循环中使用 breakcontinue 语句:

#!/bin/bash
i=1
while [ $i -le 100 ]
do
    if [ $i -eq 3 ]
    then
        echo "[跳过] 检测到 i=3,跳过本轮执行"
        i=$(( $i + 1 ))
        continue
    fi
    if [ $i -eq 5 ]
    then
        echo "[退出] 检测到 i=5,立即结束整个循环"
        break
    fi
    echo "[运行] 正在执行第 $i 次迭代"
    i=$(( $i + 1 ))
done

以上代码会输出以下结果:

[运行] 正在执行第 1 次迭代
[运行] 正在执行第 2 次迭代
[跳过] 检测到 i=3,跳过本轮执行
[运行] 正在执行第 4 次迭代
[退出] 检测到 i=5,立即结束整个循环