一、Shell 里的循环主要分三种

原笔记把循环分为:

  • for:最常用,适合遍历清单
  • while:更灵活,适合条件判断、死循环、读文件
  • until:极少用,但需要知道

这三者的区别不在语法本身,而在于“你要处理的是列表、条件还是持续等待”。

二、for 循环最适合做批量遍历

2.1 最常用的 for 格式

for n in {1..10}
do
    echo $n
done

候选清单可以来自很多地方:

  • 手写列表
  • 花括号扩展,例如 {1..10}{a..e}
  • seq
  • 命令结果

2.2 C 风格 for

for((i=1;i<=10;i++))
do
    echo $i
done

这种写法在数组遍历时尤其常见。

三、案例:批量创建随机文件

原笔记给出的经典题目,是在 /oldboy 目录下批量创建 10 个随机文件名的 HTML 文件:

#!/bin/bash
dir=/oldboy/

[ -d $dir ] || mkdir $dir

for n in {1..10}
do
    filename_suiji=$(mkpasswd -l 10 -s 0)
    filename_full=${filename_suiji}_oldboy.html
    cd $dir
    touch $filename_full
done

这个案例很好地体现了 for 的价值:同一组动作,用循环一次性处理多个对象。

四、while 循环更适合做条件处理

4.1 基础格式

while 条件
 do
    命令
 done

while 的关键是:只要条件成立,就持续执行。

4.2 输出 1 到 10

#!/bin/bash
i=1
while [ $i -le 10 ]
do
    echo $i
    let i++
done

4.3 计算 1 到 100 的和

#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
    let sum=sum+i
    let i++
done

echo $sum

这个案例也说明:循环和整数运算通常是一起出现的。

五、死循环最适合做持续交互

原笔记用“猜数字游戏”说明了死循环的典型应用场景:持续读入、持续判断,直到满足退出条件。

#!/bin/bash
guess_num=$((RANDOM % 100 + 1))
i=0

check_percent() {
    if [ $i -le 3 ]; then
        echo "您超越了99.99%的人"
    elif [ $i -ge 4 -a $i -le 6 ]; then
        echo "您超越了80%的人"
    else
        echo "您超越了70%的人"
    fi
}

while true; do
    read -p "请输入你猜测数字: " num

    if ! [[ "$num" =~ ^[0-9]+$ ]]; then
        echo "请输入数字"
        continue
    fi

    let i++

    if [ $num -eq $guess_num ]; then
        echo "恭喜您猜对了"
        check_percent
        exit 0
    fi

    if [ $num -gt $guess_num ]; then
        echo "数字过大,请换一个更小的数字"
    else
        echo "数字过小,请换一个更大的数字"
    fi
done

这个脚本把循环、正则判断、函数、分支判断都串起来了,是一份很好的综合练习。

六、while read 是读文件的高频写法

原笔记对读文件场景给了 3 种方式,但最推荐的是在 done 后面直接接输入重定向:

while read line
do
    echo $line
done < file.txt

原因是这种写法不会额外开子 Shell,循环里修改的变量在后面还能继续使用。

6.1 案例:统计文件行数

#!/bin/bash
file=num.txt
i=0

while read line
do
    let i++
done < $file

echo "$file 文件一共有 $i 行"

这已经是处理配置文件、主机列表、日志结果文件时的标准动作。

七、案例:日志分析后做防 DOS 处理

原笔记里最像真实生产脚本的循环案例,是先统计访问日志,再逐行处理异常 IP:

#!/bin/bash
access_log="/var/log/nginx/access.log-20240104"
result_file="/server/files/result.txt"

awk '{print $1}' "$access_log" | sort | uniq -c | sort -rn | head -5 > "$result_file"

while read line
do
    ip_cnt=$(echo "$line" | awk '{print $1}')
    ip_addr=$(echo "$line" | awk '{print $2}')

    if [ "$ip_cnt" -ge 200 -a $(iptables -nL | grep -wc "${ip_addr}") -eq 0 ]
    then
        iptables -t filter -I INPUT -s "$ip_addr" -j DROP
    fi
done < "$result_file"

这个案例体现了 Shell 自动化的典型组合拳:

  • 三剑客先做统计
  • while read 逐行读取结果
  • if 判断是否满足封禁条件
  • iptables 执行动作

八、until 和循环控制语句也要会用

原笔记虽然强调 until 很少用,但还是给出了基本格式:

until 条件
 do
    命令
 done

它和 while 的思路相反:只要条件不满足就继续执行。

另外 4 个循环相关的控制语句也必须认识:

  • exit:终止脚本
  • return:终止函数并返回状态码
  • break:退出循环
  • continue:跳过本次循环

例如:

for n in {1..10}
do
    [ $n -eq 5 ] && break
    echo $n
done

for n in {1..10}
do
    [ $n -eq 5 ] && continue
    echo $n
done

九、循环这章真正的重点

把原笔记所有案例看完后,可以把循环理解成下面 3 种能力:

  • for:批量遍历一组对象
  • while:持续处理条件或输入
  • while read:批量读取文件并执行动作

运维自动化里,真正有价值的不是“会写循环语法”,而是能把循环和判断、三剑客、系统命令组合起来,批量完成原本需要手工重复的操作。