跳转到内容

Shell循环结构(for/while)

课程介绍

循环是编程语言的核心功能之一,Shell脚本通过for循环和while循环实现重复执行代码块。本课程将详细讲解Shell循环的语法、使用场景以及break和continue语句,帮助你掌握Shell脚本中的循环控制逻辑。

1. for循环

1.1 for循环基本语法

Shell的for循环有多种语法形式。

语法1:遍历列表

bash
for 变量 in 列表
do
    代码块
done

语法2:遍历文件

bash
for 变量 in 文件模式
do
    代码块
done

语法3:C语言风格

bash
for ((初始化; 条件; 更新))
do
    代码块
done

1.2 遍历列表

for循环最常用的场景是遍历列表。

示例1:遍历数字列表

bash
#!/bin/bash
# 遍历数字列表
for num in 1 2 3 4 5
do
    echo "数字:$num"
done

示例2:遍历字符串列表

bash
#!/bin/bash
# 遍历字符串列表
for fruit in 苹果 香蕉 橙子 葡萄
do
    echo "水果:$fruit"
done

示例3:使用seq生成序列

bash
#!/bin/bash
# 使用seq生成序列
for num in $(seq 1 10)
do
    echo "数字:$num"
done

1.3 遍历文件

for循环可以遍历文件和目录。

示例1:遍历文件

bash
#!/bin/bash
# 遍历当前目录的所有文件
for file in *.txt
do
    echo "文件:$file"
done

示例2:遍历目录

bash
#!/bin/bash
# 遍历当前目录的所有目录
for dir in */
do
    echo "目录:$dir"
done

示例3:递归遍历文件

bash
#!/bin/bash
# 递归遍历文件
for file in $(find /data -name "*.txt")
do
    echo "文件:$file"
done

1.4 C语言风格的for循环

Shell支持类似C语言的for循环语法。

示例1:数字循环

bash
#!/bin/bash
# C语言风格的for循环
for ((i=1; i<=10; i++))
do
    echo "数字:$i"
done

示例2:步长循环

bash
#!/bin/bash
# 步长为2的循环
for ((i=0; i<=10; i+=2))
do
    echo "数字:$i"
done

示例3:倒序循环

bash
#!/bin/bash
# 倒序循环
for ((i=10; i>=1; i--))
do
    echo "数字:$i"
done

2. while循环

2.1 while循环基本语法

while循环在条件为真时重复执行代码块。

基本语法

bash
while [ 条件 ]
do
    代码块
done

2.2 while循环示例

示例1:数字循环

bash
#!/bin/bash
# while循环
i=1
while [ $i -le 10 ]
do
    echo "数字:$i"
    i=$((i+1))
done

示例2:读取文件

bash
#!/bin/bash
# 读取文件
while read line
do
    echo "行:$line"
done < /etc/passwd

示例3:无限循环

bash
#!/bin/bash
# 无限循环
while true
do
    echo "Hello, World!"
    sleep 1
done

2.3 until循环

until循环在条件为假时重复执行代码块。

基本语法

bash
until [ 条件 ]
do
    代码块
done

示例

bash
#!/bin/bash
# until循环
i=1
until [ $i -gt 10 ]
do
    echo "数字:$i"
    i=$((i+1))
done

3. break和continue

3.1 break语句

break语句用于跳出循环。

示例1:跳出for循环

bash
#!/bin/bash
# 跳出for循环
for i in {1..10}
do
    if [ $i -eq 5 ]; then
        break
    fi
    echo "数字:$i"
done

示例2:跳出while循环

bash
#!/bin/bash
# 跳出while循环
i=1
while true
do
    if [ $i -gt 10 ]; then
        break
    fi
    echo "数字:$i"
    i=$((i+1))
done

3.2 continue语句

continue语句用于跳过当前循环迭代。

示例1:跳过for循环的迭代

bash
#!/bin/bash
# 跳过for循环的迭代
for i in {1..10}
do
    if [ $((i % 2)) -eq 0 ]; then
        continue
    fi
    echo "奇数:$i"
done

示例2:跳过while循环的迭代

bash
#!/bin/bash
# 跳过while循环的迭代
i=1
while [ $i -le 10 ]
do
    i=$((i+1))
    if [ $((i % 2)) -eq 0 ]; then
        continue
    fi
    echo "奇数:$i"
done

4. 循环嵌套

4.1 嵌套for循环

for循环可以嵌套使用。

示例1:九九乘法表

bash
#!/bin/bash
# 九九乘法表
for i in {1..9}
do
    for j in {1..9}
do
        if [ $j -le $i ]; then
            echo -n "$j*$i=$((i*j)) "
        fi
    done
    echo ""
done

示例2:遍历目录和文件

bash
#!/bin/bash
# 遍历目录和文件
for dir in */
do
    echo "目录:$dir"
    for file in $dir/*
    do
        echo "  文件:$file"
    done
done

4.2 嵌套while循环

while循环也可以嵌套使用。

示例

bash
#!/bin/bash
# 嵌套while循环
i=1
while [ $i -le 3 ]
do
    echo "外层循环:$i"
    j=1
    while [ $j -le 3 ]
    do
        echo "  内层循环:$j"
        j=$((j+1))
    done
    i=$((i+1))
done

5. 完整示例

示例1:批量重命名文件

bash
#!/bin/bash
# 批量重命名文件

# 遍历所有.txt文件
for file in *.txt
do
    # 获取文件名(不含扩展名)
    filename=$(basename "$file" .txt)
    
    # 重命名文件
    mv "$file" "${filename}.bak"
    
    echo "重命名:$file -> ${filename}.bak"
done

示例2:批量创建用户

bash
#!/bin/bash
# 批量创建用户

# 用户列表
users=("user1" "user2" "user3" "user4" "user5")

# 遍历用户列表
for user in "${users[@]}"
do
    # 检查用户是否已存在
    if id "$user" &>/dev/null; then
        echo "用户 $user 已存在"
        continue
    fi
    
    # 创建用户
    useradd "$user"
    
    # 设置密码
    echo "$user:123456" | chpasswd
    
    echo "用户 $user 创建成功"
done

示例3:批量处理文件

bash
#!/bin/bash
# 批量处理文件

# 遍历所有.log文件
for file in *.log
do
    # 统计行数
    lines=$(wc -l < "$file")
    
    # 统计错误数量
    errors=$(grep -c "ERROR" "$file")
    
    # 输出统计信息
    echo "文件:$file"
    echo "  行数:$lines"
    echo "  错误数:$errors"
    echo ""
done

示例4:监控目录变化

bash
#!/bin/bash
# 监控目录变化

# 监控目录
dir="/data/upload"

# 获取初始文件列表
old_files=$(ls "$dir")

# 无限循环
while true
do
    # 获取当前文件列表
    new_files=$(ls "$dir")
    
    # 比较文件列表
    if [ "$old_files" != "$new_files" ]; then
        echo "目录 $dir 发生变化"
        echo "  新文件列表:$new_files"
        
        # 更新文件列表
        old_files=$new_files
    fi
    
    # 等待1秒
    sleep 1
done

示例5:批量下载文件

bash
#!/bin/bash
# 批量下载文件

# 文件列表
files=("file1.txt" "file2.txt" "file3.txt")

# 下载目录
download_dir="/data/download"

# 创建下载目录
mkdir -p "$download_dir"

# 遍历文件列表
for file in "${files[@]}"
do
    # 下载文件
    wget "https://example.com/$file" -O "$download_dir/$file"
    
    # 检查下载是否成功
    if [ $? -eq 0 ]; then
        echo "下载成功:$file"
    else
        echo "下载失败:$file"
    fi
done

6. 循环性能优化

6.1 避免在循环中执行耗时操作

bash
# 不好的做法
for file in *.txt
do
    # 在循环中执行耗时操作
    count=$(find . -name "*.txt" | wc -l)
done

# 好的做法
# 在循环外执行耗时操作
count=$(find . -name "*.txt" | wc -l)
for file in *.txt
do
    echo "文件:$file"
done

6.2 使用数组代替多个变量

bash
# 不好的做法
file1="file1.txt"
file2="file2.txt"
file3="file3.txt"

# 好的做法
files=("file1.txt" "file2.txt" "file3.txt")
for file in "${files[@]}"
do
    echo "文件:$file"
done

6.3 使用并行处理

bash
# 使用xargs并行处理
find . -name "*.txt" | xargs -P 4 -I {} sh -c 'echo "处理:{}"'

# 使用GNU parallel并行处理
find . -name "*.txt" | parallel -j 4 echo "处理:{}"

课程总结

这节课我们学习了Shell循环结构。

核心内容:

  • for循环(遍历列表、遍历文件、C语言风格)
  • while循环
  • until循环
  • break和continue语句
  • 循环嵌套
  • 完整示例
  • 循环性能优化

重要命令:

  • for ... in ... do ... done:for循环
  • while ... do ... done:while循环
  • until ... do ... done:until循环
  • break:跳出循环
  • continue:跳过当前迭代
  • seq:生成序列
  • find:查找文件

循环是Shell脚本编程的核心,掌握这些知识后,我们将在后续课程中学习函数、文件操作等高级特性。

课后练习

练习1(基础)

创建一个脚本,要求:

  1. 使用for循环输出1-10的数字
  2. 使用while循环输出1-10的数字
  3. 使用until循环输出1-10的数字

练习2(进阶)

创建一个脚本,要求:

  1. 遍历当前目录的所有.txt文件
  2. 统计每个文件的行数
  3. 输出文件名和行数

练习3(拓展)

创建一个脚本,要求:

  1. 使用嵌套循环输出九九乘法表
  2. 使用for循环和while循环实现

评论区

专业的Linux技术学习平台,从入门到精通的完整学习路径