shell的三种循环

  • 当你需要遍历一个已知的列表(文件、数字、字符串)时,首选 for 循环。

  • 当你需要在某个条件持续为真时执行任务,或读取文件时,使用 while 循环。

  • 当你需要等待某个条件变为真,或者逻辑上更符合“直到...才停止”的思维时,使用 until 循环。

  • 核心区别速览

    循环类型核心逻辑适用场景
    for 循环遍历一个已知的列表或范围处理文件列表、数字序列等明确集合中的每一个元素。
    while 循环条件为“真”时执行守护进程、读取文件、计数器循环等,条件满足则持续运行
    until 循环直到条件为“真”时停止等待某个条件成立(如等待服务启动、文件生成),条件不满足则持续运行

1. for 循环:遍历的艺术

for 循环擅长处理已知的列表。Bash 支持多种风格。

a. 列表for循环

遍历一个明确的元素列表。

# 遍历由空格分隔的单词列表

for fruit in apple banana orange

do

         echo "I like $fruit"

done

b. 使用{start..end}seq生成序列

这是生成数字序列的常用方法。

# 使用 {起始..结束} 生成序列
for i in {1..5}
do
    echo "Number: $i"
done

# 使用 seq 命令,功能更强大
for i in $(seq 1 2 10)  # 从1到10,步长为2
do
    echo "Odd number: $i"
done

c. 类C风格的for循环

与C语言语法类似,适合复杂的计数循环。

bash

# 初始化变量; 条件判断; 变量更新
for ((i=1; i<=5; i++))
do
    echo "Counter: $i"
done

d. 遍历命令输出

这是Shell脚本中非常强大的用法。

# 遍历当前目录下的所有文件
for file in *
do
    echo "Found file: $file"
done

# 遍历特定命令的输出
for user in $(cat /etc/passwd | cut -d: -f1)
do
    echo "User: $user"
done

    2. while 循环:条件为真时执行

    while 语法规则:

    while 命令; do ... done
    

    while 会执行后面这条命令,命令返回值为 0(真)就继续循环,非 0(假)就退出

    a. while 计数器循环(数字循环,替代 for ((i;...)))

    适用:需要自定义步长、循环次数、带判断的数字遍历 逻辑:手动定义计数器,循环内更新变量,条件成立就执行

    i=1
    while [ $i -le 5 ]
    do
        echo "计数:$i"
        i=$((i+1))  # 计数器自增,不写就是死循环
    done
    

    • Shell:++ 运算符只能放在双括号 (( )) 内部,单独写在赋值外面无效。
    写法是否可用兼容性
    i=$i++❌ 报错完全不行
    i=$((i+1))✅ 推荐所有 shell 通用
    i=$((i++))✅ 可用bash 通用
    ((i++))✅ 最简仅 bash

    适用场景:分批循环处理数据、重试接口 N 次、定时循环指定次数

    b. while 读取文件 / 命令输出(运维最常用!)

    适用:逐行读取日志、配置、/etc/passwd、命令输出,比 for i in $(xxx) 更稳定,不会丢空格、不会撑爆内存

    # 读取文件每行
    while read line
    do
        echo "一行内容:$line"
    done < /etc/passwd
    
    # 读取管道命令输出
    cut -d: -f1 /etc/passwd | while read user
    do
        echo "用户名:$user"
    done
    

    优势:流式读取,一行一行处理超大文件不卡顿,处理带空格的文本不会错乱,写日志分析、批量操作账号必备。

    核心特点:一行一行流式读取,超大文件不卡顿,能保留空格、特殊字符

    实际工作场景

    1. 遍历系统账号、批量操作用户 读取 /etc/passwd 批量创建家目录、修改密码、禁用闲置账号(你之前写的脚本)
    2. 分析日志文件 逐行过滤 nginx / 系统日志,统计报错 IP、404 请求、登录失败记录
    3. 批量处理配置清单 新建 ip_list.txt 存放服务器 IP,循环 ssh 批量执行命令、推送文件

      txt

      示例 1:过滤 /var/log/messages 系统报错日志

      #!/bin/bash
      # 逐行读取系统日志
      while read log_line
      do
          # 判断:当前行包含 error / fail 关键字
          if echo "$log_line" | grep -iE "error|fail";then
              echo "发现异常日志:$log_line"
          fi
      # 日志文件重定向给while循环
      done < /var/log/messages
    4. 192.168.1.10
      192.168.1.11
      192.168.1.12
      
      while read ip
      do
          ssh root@$ip "systemctl restart nginx"
      done < ip_list.txt
      
    5. 读取 CSV / 冒号分隔配置文件 数据库账号、业务配置按行解析,拆分字段入库
    6. 管道接收命令输出 docker ps | while read 容器信息,批量停止、删除过期容器

    c. while 无限循环 / 死循环

    两种写法:while true / while :: 是 shell 空命令,永远返回真)

    # 无限循环,靠内部 break 退出
    while true
    do
        echo "持续运行中,输入q退出"
        read input
        if [ "$input" = "q" ];then
            break  # 跳出循环,否则永久循环
        fi
        sleep 1
    done
    

    适用场景:后台监控脚本、端口持续探测、菜单交互、定时轮询;

    ⚠️ 注意:必须加退出条件(break),否则纯死循环卡死终端。

    实际工作场景

    1. 服务监控脚本(最常用) 持续检测 nginx、mysql 进程挂没挂,一旦停止自动重启
      while true
      do
          pgrep nginx >/dev/null || systemctl start nginx
          sleep 3
      done
      
    2. 端口 / 健康状态巡检 每隔 5 秒检测业务端口通不通,异常发送告警(邮件 / 钉钉)
    3. 交互式菜单脚本 运维工具箱:输入数字 1 查看磁盘、2 清理日志、0 退出,循环等待用户输入
    4. 消息队列轮询消费 持续读取消息,有任务就处理,无任务休眠,程序 7×24 小时后台运行
    5. 实时同步文件 循环检测目录新文件,自动同步到备份服务器

    实际工作场景:

    • 接口 / 命令重试 N 次 调用 API、远程 ssh、数据库连接容易失败,最多重试 3 次就放弃,不用无限等
      i=1
      while ((i <= 3))
      do
          curl http://127.0.0.1:8080/health
          if [ $? -eq 0 ];then break; fi
          echo "第$i次失败,等待2秒重试"
          sleep 2
          ((i++))
      done
      
    • 分批处理文件 / 数据 日志有 10000 行,每次只处理 100 条,分批次循环,防止一次性占满内存
    • 遍历有序编号文件 文件命名 log1.log、log2.log…log20.log,按数字顺序逐个读取分析
    • 定时循环固定次数 压测脚本:循环发请求 500 次后自动停止

    二、三者实用度对比

    1. b 读取文件内容(第一实用) 运维处理日志、配置、批量遍历文件全靠它,生产脚本出现频率最高,是 while 区别于 for 循环的核心优势。
    2. a 计数器循环(第二实用) 当 C 风格 for 不好写复杂判断时用,比如循环失败重试 3 次、分批导出数据。
    3. c 无限循环(第三实用) 多用于监控、交互式菜单,不是通用遍历需求,使用场景相对窄一点。

    快速选择对照表(工作时直接对照)

    表格

    场景需求选用哪一种 while
    只循环固定 N 次(重试、分批、编号文件)a 计数器循环
    处理文本、日志、IP 清单、系统配置文件b while read 读文件
    持续监控、后台常驻、交互式菜单、永久轮询c while true 无限循环

    补充区分:什么时候不用 while,改用 for

    1. 简单数字 1~10:优先 for ((i=1;i<=10;i++))
    2. 匹配文件名 *.logfor file in *.log
    3. 只要读文件、超大日志、带空格文本,一律用 while read(for 循环会出 bug)

    三、补充:为什么这三类是核心,别的 while 很少用

    while 的本质逻辑:判断命令返回码,返回 0(真)就执行循环体 所有 while 写法都只是「判断条件不同」衍生出来的:

    • 判断数字大小 → 计数器循环 (a)
    • 判断 read 读取是否成功(读到文件末尾返回假)→ 读文件 (b)
    • 判断恒成立(true/:)→ 无限循环 (c)

    其他小众用法(比如 while 判断文件是否存在、while 判断进程是否存活)本质上都属于无限循环的细分变种,不会单独划为一类。

    四、配套区分:while 和 for 怎么选

    • 简单数字序列(1~10):优先 for ((i=1;i<=10;i++))
    • 逐行读文件 / 日志:必须 while read(for 有缺陷)
    • 持续轮询、后台监控、交互菜单:while true 无限循环

    3. until 循环:直到条件为真时停止

    until 循环的逻辑与 while 相反:它会在条件测试为假(返回状态码非0)时持续执行,直到条件为真才停止

    1. a:计数器循环(等价 while 计数,只是判断逻辑反过来)

    单纯数字循环场景,until 完全能替代 while,只是判断条件要写反向。 示例:打印 1~5

    i=1
    # 直到i>5成立就停
    until [ $i -gt 5 ]
    do
      echo $i
      ((i++))
    done
    

    工作里几乎没人用 until 做纯计数,因为 while 逻辑更直观,只有为了练习语法才会这么写。

    2. b:等待某个条件成立(until 真正的核心、高频实战场景)

    这是 until 不可替代的优势,语义天然匹配「等待 XX 出现就停止」,可读性碾压 while 取反。 典型运维场景:

    • 等待端口监听(mysql/nginx 启动)
    • 等待文件生成
    • 等待主机 ping 通
    • 等待接口健康检查返回成功 示例:等待 3306 端口出现
    until ss -lnp | grep 3306 &>/dev/null
    do
      echo "mysql未启动,等待2秒"
      sleep 2
    done
    echo "mysql就绪"
    


    补充:有没有第三种用法?

    没有独立第三类,只有一个衍生用法:交互式等待输入退出,本质还是「等待条件成立(输入 q)」,属于 b 类等待场景的分支,不算独立分类。

    until [ "$input" = "q" ]
    do
      read input
    done
    

    实操选择建议

    1. 单纯计数、批量遍历:只用 while / C 风格 for,不用 until;
    2. 脚本需要等待某个资源就绪再往下执行:优先 until,代码更好看懂;
    3. 无限常驻监控(一直跑不主动停):只用 while true,不用 until。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值