Shell for 循环

《Shell for 循环》深度学习版 🧠

🎉 欢迎来到 Shell 编程中极为关键的一章 —— for 循环。它是 Linux Shell 脚本中最常用的迭代结构,是实现“批量处理、重复操作”的核心工具。掌握它,你就能写出真正智能、自动化的脚本!


🎯 学习目标

  • 理解 for 循环的基本语法与执行流程
  • 掌握传统 for in list 和 C 风格 for (( )) 的写法
  • 能在实际脚本中灵活使用通配符、命令替换、数组等迭代方式
  • 学会结合 if 判断、breakcontinue 实现复杂逻辑控制
  • 避免常见陷阱和语法错误,写出健壮可靠的 Shell 迭代语句

⭐ 核心重点(知识点速览)

类型 内容 描述
基本语法 for var in list; do ... done 最常用的传统循环结构
C 风格语法 for ((i=start; i<=end; i++)); do ... done 更适合数值范围的循环
变量列表 "item1" "item2"$(ls) 支持变量、命令输出、数组等
控制结构 break, continue 控制循环流程
通配符 *.txt 批量处理文件时非常实用
数组遍历 ${arr[@]} 结合数组实现更复杂的迭代
不同 shell 差异 bash vs dash 注意兼容性问题

📖 详细讲解


一、基本语法结构

✅ 传统 for 循环格式:

for var in item1 item2 item3 ...
do
    # 循环体
done

✅ 示例:遍历字符串列表

for name in Alice Bob Charlie
do
    echo "欢迎用户:$name"
done

输出:

欢迎用户:Alice
欢迎用户:Bob
欢迎用户:Charlie

二、C 风格 for 循环(Bash 特有)

适用于数字范围的循环,语法类似 C 语言。

for ((i=1; i<=5; i++))
do
    echo "第 $i 次循环"
done

💡 该语法只能在 Bash 中使用,dash/sh 不支持。


三、使用通配符批量处理文件 📁

这是最常用于自动化任务的技巧之一。

✅ 示例:批量重命名 .log 文件为 .bak

for file in *.log
do
    mv "$file" "${file%.log}.bak"
done

💡 解释:

  • *.log 匹配当前目录下所有 .log 文件
  • ${file%.log} 表示去掉文件名后缀 .log

四、结合命令替换进行动态迭代 🔍

你可以将命令的结果作为循环的输入。

✅ 示例:列出所有正在运行的服务名称

for service in $(systemctl list-units --type=service --state=running | grep -v 'Loaded' | awk '{print $1}')
do
    echo "服务正在运行:$service"
done

五、结合数组进行高级迭代 🧱

数组非常适合存储多个元素并逐个处理。

✅ 示例:定义数组并遍历

users=("Alice" "Bob" "Charlie")
for user in "${users[@]}"
do
    echo "用户:$user"
done

六、嵌套循环与控制结构 🔄

你可以在一个循环内部再嵌套另一个循环,也可以使用 breakcontinue 控制流程。

✅ 示例:打印乘法表(9x9)

for i in {1..9}
do
    for j in $(seq 1 $i)
    do
        echo -n "$j*$i=$((i*j))  "
    done
    echo ""
done

✅ 示例:跳过特定值

for num in {1..10}
do
    if [ $num -eq 5 ]; then
        continue   # 跳过数字 5
    fi
    echo "数字:$num"
done

七、实战案例分析 🧪

🎯 场景一:批量创建用户 + 设置密码

users=("alice" "bob" "charlie")

for user in "${users[@]}"
do
    if id "$user" &>/dev/null; then
        echo "⚠️ 用户 $user 已存在"
    else
        useradd "$user"
        echo "password123" | passwd "$user" --stdin &>/dev/null
        echo "✅ 用户 $user 已创建"
    fi
done

🎯 场景二:清理临时文件 + 输出日志

log_file="/var/log/cleaner.log"
timestamp=$(date "+%Y-%m-%d %T")

echo "[$timestamp] 开始清理 /tmp 下的 .tmp 文件..." >> "$log_file"

count=0
for file in /tmp/*.tmp
do
    rm -f "$file"
    count=$((count + 1))
done

echo "[$timestamp] 共删除 $count 个临时文件" >> "$log_file"

🎯 场景三:检查服务器状态 + 发送通知

servers=("web01" "db01" "cache01")

for server in "${servers[@]}"
do
    ping -c 1 "$server" &>/dev/null
    if [ $? -eq 0 ]; then
        echo "🟢 服务器 $server 正常"
    else
        echo "🔴 服务器 $server 不可达!"
        # 可以在此加入发送邮件或报警逻辑
    fi
done

八、不同系统的差异与注意事项 ⚠️

项目 Bash Dash (Ubuntu 默认) zsh
支持 (( ))
支持数组 ✅(部分)
支持 continue / break
推荐脚本开头 #!/bin/bash #!/bin/sh #!/bin/zsh
推荐写法 使用 for in 使用 POSIX 兼容写法 使用高级特性

📝 小贴士:

  • 在 Ubuntu 系统中,默认 /bin/shdash,不支持 (( )),所以如果你要使用高级特性,请确保脚本开头为 #!/bin/bash
  • 使用双引号包裹变量,防止路径含空格出错。
  • 多层循环建议用缩进保持可读性。

⚠️ 常见陷阱 & 解决方案

问题 原因 解决方法
command not found 使用了 dash 不支持的语法 改为 #!/bin/bash
unexpected token 语法错误,如缺少 dodone 检查括号和关键字是否正确
无法匹配文件 通配符未加引号导致展开失败 使用双引号或测试是否存在文件
变量未更新 在子 shell 中修改变量 使用全局变量或函数封装
循环未执行 列表为空 加入判断或调试输出

🧠 总结与小贴士

技巧 说明
for 是 Shell 的“发动机” 让脚本能批量执行任务
推荐使用 for in list 兼容性最好
使用 (( )) 更适合数值范围 Bash 特有功能
始终对变量加双引号 防止空格或未定义变量出错
结合 if 构建复杂逻辑 让脚本具备“判断能力”
善用通配符和数组 提升灵活性
避免直接在循环中频繁调用外部命令 影响性能,应尽量合并处理

📚 推荐练习题(可选)

  1. 编写一个脚本,遍历 /etc/passwd 中的所有用户,并输出 UID 和用户名。
  2. 写一个脚本,遍历指定目录下的所有 .txt 文件,并统计每个文件的行数。
  3. 实现一个脚本,生成从 1 到 100 的奇数列表。
  4. 编写一个脚本,检查系统中所有用户是否都有家目录。
  5. 创建一个备份脚本,自动压缩并归档 /var/log/ 目录中的 .log 文件。

🎯 下一章预告:《Shell while/until 循环》

你已经掌握了如何进行批量处理和迭代操作,下一步我们将学习如何根据条件来决定是否继续循环 —— 使用 whileuntil,让你的脚本真正“动起来”,并且能响应实时变化的数据!

需要我继续为你生成下一章内容吗?😊

posted @ 2025-06-18 22:35  红尘过客2022  阅读(200)  评论(0)    收藏  举报