Appearance
06|循环语句
上一篇的条件判断让脚本能做"二选一"。但运维里很多事要重复做——遍历一批文件逐个处理、对十台机器逐台执行命令、每隔几秒检查一次服务状态。这些靠循环。
本篇讲 Shell 的三种循环:for、while、until。
一、for 遍历列表
for 遍历一组值,最常见的循环:
bash
for host in web-01 web-02 db-01; do
echo "处理: $host"
ssh "$host" "uptime"
donetext
处理: web-01
14:30 up 47 days...
处理: web-02
...遍历数字范围用 seq 或花括号:
bash
# seq 生成 1 到 5
for i in $(seq 1 5); do
echo "第 $i 次"
done
# 花括号展开
for i in {1..5}; do
echo "第 $i 次"
done遍历文件列表——比如处理 /var/log 下所有 .log 文件:
bash
for logfile in /var/log/*.log; do
echo "处理: $logfile"
wc -l "$logfile"
done如果匹配不到文件,/var/log/*.log 会原样传给循环体。要处理这种情况:
bash
shopt -s nullglob # 匹配不到时展开为空,不传通配符字面值
for logfile in /var/log/*.log; do
echo "$logfile"
done二、while 条件循环
while 在条件为真时一直循环,适合"不知道要循环几次,直到某个条件不满足"的场景:
bash
# 每隔 2 秒检查一次 nginx 是否起来
while ! systemctl is-active --quiet nginx; do
echo "等待 nginx 启动..."
sleep 2
done
echo "nginx 已启动"读文件逐行处理是 while 的经典用法:
bash
while IFS= read -r line; do
echo "行内容: $line"
done < /etc/hosts这个写法有几个细节:IFS= 防止行首尾空格被吃掉,-r 防止反斜杠被转义,< file 从文件读入。这是逐行读文件的标准写法,背下来。
三、until 反向条件循环
until 和 while 相反——条件为假时循环,直到条件为真:
bash
# 等到磁盘使用率降到 80% 以下
until [[ "$(df / | awk 'NR==2 {gsub(/%/,"");print $5}')" -lt 80 ]]; do
echo "磁盘还高,等待清理..."
sleep 10
doneuntil 用得比 while 少,但在"等到某事发生"的场景下比 while ! 更直观。
四、break 和 continue
break 跳出循环,continue 跳过本次进下一次:
bash
for host in web-01 web-02 db-01; do
if ! ssh "$host" "true" 2>/dev/null; then
echo "$host 连不上,跳过"
continue
fi
echo "处理 $host"
ssh "$host" "uptime"
# 假设遇到某个条件就停止
if ssh "$host" "test -f /tmp/stop"; then
echo "$host 标记停止,中止"
break
fi
done五、C 风格的 for
计数循环可以用 C 风格的 for:
bash
for ((i=0; i<3; i++)); do
echo "重试 $i"
done或者用 while:
bash
count=0
while [[ $count -lt 3 ]]; do
echo "重试 $count"
((count++))
done(( )) 做算术运算,((count++)) 自增。
六、循环里的常见坑
循环里读不到外部变量——管道会启动子 shell:
bash
count=0
echo -e "a\nb\nc" | while read line; do
count=$((count + 1))
done
echo "count=$count" # 0!管道右边的 while 在子 shell 里跑,对 count 的修改不影响外层。解决办法用进程替换或重定向:
bash
count=0
while read line; do
count=$((count + 1))
done < <(echo -e "a\nb\nc") # 进程替换,不启动子 shell
echo "count=$count" # 3循环体里漏分号——do 和 done 要成对:
bash
for i in 1 2 3; do # do 前要有分号
echo $i
done七、批量执行的实际例子
对一组机器执行相同命令,把循环和参数结合起来:
bash
#!/usr/bin/env bash
set -euo pipefail
command=${1:?用法: $0 <命令> [主机...]}
shift
hosts=("$@")
if [[ ${#hosts[@]} -eq 0 ]]; then
echo "没有指定主机" >&2
exit 1
fi
for host in "${hosts[@]}"; do
echo "=== $host ==="
if ssh "$host" "$command"; then
echo "成功"
else
echo "失败" >&2
fi
done跑一下:
bash
./batch.sh "uptime" web-01 web-02 db-01对三台机器逐个执行 uptime,成功失败都记录。这就是批量运维脚本的基本骨架。
学会了之后
for 遍历列表、while 条件循环、until 反向等待,加上 break/continue 控制——循环让脚本能批量处理、能重复执行、能等待状态。配合前面学的变量、条件、参数,已经能写出不少实用脚本了。
下一篇讲数组——一次存一组值、按索引访问。