Shell while 语句

《Shell while 语句》深度学习版 🧠

🎉 欢迎来到 Shell 编程中极为关键的一章 —— while 循环。它是 Linux Shell 脚本中最常用的条件循环结构,是实现“持续监听、实时处理”的核心工具。掌握它,你就能写出真正智能、自动化的脚本!


🎯 学习目标

  • 理解 while 循环的基本语法与执行流程
  • 掌握如何用 while 实现无限循环、读取输入流、监控状态等场景
  • 能在实际脚本中灵活使用文件、变量、命令返回值作为判断条件
  • 学会结合 read, tail -f, ps, grep 等命令进行实时监控
  • 避免常见陷阱和语法错误,写出健壮可靠的 Shell 条件循环语句

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

类型 内容 描述
基本语法 while condition; do ... done 条件为真时持续执行循环体
条件类型 变量、命令、test 表达式 控制循环是否继续
无限循环 while true; do ... done 用于后台服务或监控
输入流处理 while read line; do ... done < file 逐行读取文件或管道
实时监控 tail -f, ping, ps 结合 while 实现实时输出
控制结构 break, continue 控制循环流程
不同 shell 差异 bash vs dash 注意兼容性问题

📖 详细讲解


一、基本语法结构

while condition
do
    # 循环体
done

只要 condition 的退出状态码为 0(即为真),循环就会一直执行。


二、简单示例:数字递增计数器

✅ 示例:打印从 1 到 5 的数字

i=1
while [ $i -le 5 ]
do
    echo "当前数字:$i"
    i=$((i + 1))
done

💡 这是最基础的数值控制方式,常用于有限次循环任务。


三、无限循环(Infinite Loop)🔥

适用于需要持续运行的任务,如守护进程、日志监控、定时检查等。

✅ 示例:每秒显示系统负载

while true
do
    load=$(uptime | awk '{print $10}')
    echo "[$(date +"%T")] 当前系统负载:$load"
    sleep 1
done

⚠️ 注意:

  • 使用 Ctrl+C 终止此类脚本。
  • 可以加入 if 判断,在满足条件时用 break 退出循环。

四、读取文件内容逐行处理 📄

这是最常用的数据处理技巧之一。

✅ 示例:读取用户列表并创建用户

while read user
do
    if ! id "$user" &>/dev/null; then
        useradd "$user"
        echo "✅ 用户 $user 已创建"
    else
        echo "⚠️ 用户 $user 已存在"
    fi
done < users.txt

💡 users.txt 文件格式如下:

alice
bob
charlie

五、结合管道符读取命令输出 🔍

你可以将任意命令的输出作为 while 的输入源。

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

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

六、实时日志监控实战 📊

✅ 示例:实时监控 Apache 日志中的 404 错误

tail -f /var/log/apache2/access.log | while read line
do
    echo "$line" | grep -q " 404 "
    if [ $? -eq 0 ]; then
        echo "⚠️ 发现 404 错误:$line"
    fi
done

💡 解释:

  • tail -f 实时读取新增日志
  • grep -q 用于静默匹配
  • 如果发现 404 错误,则输出提示信息

七、等待某个事件发生 🕒

✅ 示例:等待某个文件出现

target_file="/tmp/ready.txt"

echo "⏳ 正在等待文件 $target_file 出现..."

while [ ! -f "$target_file" ]
do
    sleep 1
done

echo "✅ 文件已找到!继续执行后续操作..."

八、结合 read 实现交互式菜单 🧩

✅ 示例:带退出选项的菜单

while true
do
    echo "==== 主菜单 ===="
    echo "1. 创建用户"
    echo "2. 查看服务状态"
    echo "3. 退出"
    read -p "请选择操作(1-3):" choice

    case $choice in
        1)
            read -p "请输入用户名:" new_user
            useradd "$new_user" && echo "✅ 用户 $new_user 已创建"
            ;;
        2)
            systemctl status nginx
            ;;
        3)
            echo "👋 正在退出..."
            break
            ;;
        *)
            echo "❌ 无效选择,请重试"
            ;;
    esac
done

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

项目 Bash Dash (Ubuntu 默认) zsh
支持 while read
支持 tail -f
支持 (( ))
推荐脚本开头 #!/bin/bash #!/bin/sh #!/bin/zsh
注意子 shell 变量作用域

📝 小贴士:

  • 在 Ubuntu 中默认 /bin/shdash,不支持某些高级特性,建议脚本开头写成 #!/bin/bash
  • 使用双引号包裹变量,防止路径含空格出错。
  • 多层循环建议用缩进保持可读性。

十、实战案例分析 🧪

🎯 场景一:批量 Ping 主机并记录结果

cat << EOF > hosts.txt
web01
db01
cache01
EOF

while read host
do
    ping -c 1 "$host" &>/dev/null
    if [ $? -eq 0 ]; then
        echo "🟢 $host 正常"
    else
        echo "🔴 $host 不可达"
    fi
done < hosts.txt

🎯 场景二:实时监控 CPU 温度(需安装 lm-sensors)

sudo apt install lm-sensors -y
sensors | grep 'Package id 0'

while true
do
    temp=$(sensors | grep 'Package id 0' | awk '{print $4}')
    echo "[$(date +"%T")] CPU 温度:$temp"
    sleep 1
done

⚠️ 常见陷阱 & 解决方案

问题 原因 解决方法
循环未执行 条件一开始就不成立 加入调试输出或初始值设置
循环无法退出 条件永远为真 使用 break 或合理修改条件
无法读取文件 文件不存在或权限不足 添加 -f 检查或 chmod 修改权限
子 shell 中变量未更新 在管道中赋值 使用全局变量或函数封装
性能问题 高频率调用外部命令 合理调整间隔时间或合并处理逻辑

🧠 总结与小贴士

技巧 说明
while 是 Shell 的“守望者” 让脚本能持续响应变化
推荐使用 while read 处理文本数据非常强大
使用 true 构建守护循环 适合后台监控任务
始终对变量加双引号 防止空格或未定义变量出错
结合 tail -f 实现实时监控 提升脚本智能化水平
避免死循环 合理设计退出机制
注意子 shell 变量作用域 特别是在管道中使用时

📚 推荐练习题(可选)

  1. 编写一个脚本,每隔 5 秒检查一次磁盘空间,如果超过 90%,则发送邮件通知。
  2. 写一个脚本,实时监控 /var/log/auth.log,发现 SSH 登录失败超过 5 次就报警。
  3. 实现一个脚本,不断读取用户输入的数字,并累加求和,输入 q 时退出。
  4. 编写一个脚本,模拟进度条动画(使用 \rsleep)。
  5. 创建一个备份脚本,当检测到指定目录中有新文件时,自动打包上传至远程服务器。

🎯 下一章预告:《Shell until 语句》

你已经掌握了如何通过 while 实现“条件为真时持续执行”,下一步我们将学习它的反向版本 —— until 循环,让你的脚本能更灵活地应对各种条件触发任务!

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

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