Shell 脚本流程控制指南:循环逻辑与条件分支
一、迭代循环机制(For Loop)
在 Shell 脚本中,for 语句用于处理需要重复执行的操作。根据遍历对象的不同,主要分为列表迭代、参数迭代以及类 C 语言风格的计数循环三种形式。
1. 序列列表迭代
此模式适用于已知具体元素列表或数值范围的情况。它将列表中的每个值依次赋值给变量,并执行命令块。
# 基础语法结构
for current_item in sequence; do
# 处理当前项
done
场景演示: 遍历文件列表或数值区间。
#!/bin/bash
# 遍历指定目录下的脚本文件
target_dir="/opt/scripts"
for file_path in "$target_dir"/*.sh; do
echo "发现文件:$file_path"
done
# 生成网络段 IP 地址示例 (192.168.1.1 至 192.168.1.10)
base_ip="192.168.1"
for last_octet in {1..10}; do
echo "${base_ip}.${last_octet}"
done
2. 命令行参数迭代
当未指定列表时,for 循环会默认遍历传递给脚本的所有位置参数。
#!/bin/bash
count=0
for arg in "$@"; do
echo "参数 ${count}: $arg"
((count++))
done
echo "接收到的总参数数量:$#"
3. 类 C 风格计数循环
这种格式更接近传统编程语言,包含初始化、条件判断和步进表达式。
# 语法模板
for (( init_expr; condition; increment )); do
commands
done
实际应用: 构建九九乘法表。
#!/bin/bash
for (( row=1; row<=9; row++ )); do
for (( col=1; col<=row; col++ )); do
result=$((row * col))
printf "%d*%d=%-2d\t" "$col" "$row" "$result"
done
echo ""
done
视觉化案例: 绘制 8x8 棋盘格(使用 ANSI 转义码控制颜色)。
#!/bin/bash
for (( i=1; i<=8; i++ )); do
for (( j=1; j<=8; j++ )); do
sum_coord=$((i + j))
if (( sum_coord % 2 == 0 )); then
# 偶数和显示白色背景
echo -ne "\033[47m \033[0m"
else
# 奇数和显示黑色背景
echo -ne "\033[40m \033[0m"
fi
done
echo ""
done
循环控制指令
break: 终止当前循环层级的执行。continue: 跳过本次剩余代码,直接进入下一轮迭代。exit: 直接退出整个脚本进程。
筛选奇数求和示例:
#!/bin/bash
total_sum=0
for (( n=1; n<=100; n++ )); do
if (( n % 2 == 0 )); then
continue # 跳过偶数
fi
total_sum=$((total_sum + n))
done
echo "1 到 100 的奇数总和为:$total_sum"
二、条件驱动循环(While & Until)
1. While 循环
只要测试条件为真(True),就持续执行循环体。常用于不知道确切次数但满足某个条件即停止的场景。
while [ test_condition ]; do
action
done
实时监控案例: 检查用户登录状态。
#!/bin/bash
target_user="admin"
echo "开始监控用户 '$target_user'..."
while ! who | grep -q "^$target_user "; do
echo "$(date): 用户尚未上线,等待检测..."
sleep 5
done
echo "检测到用户已登录!"
2. Until 循环
逻辑与 while 相反,当条件为假(False)时执行循环,直到条件变为真才停止。
until [ test_condition ]; do
action
done
差异对比:
while: 条件满足则执行。until: 条件不满足则执行。
批量建用户实战: 前 5 个指定 UID,后 5 个指定家目录。
#!/bin/bash
counter=1
limit=10
# 确保基础目录存在
[ ! -d /rhome ] && mkdir -p /rhome
until (( counter > limit )); do
username="user${counter}"
if (( counter <= 5 )); then
# 自定义 UID (1001-1005)
uid_val=$((1000 + counter))
useradd -u "$uid_val" "$username"
echo "Created $username with UID $uid_val"
else
# 自定义家目录
home_path="/rhome/$username"
useradd -d "$home_path" "$username"
echo "Created $username with Home $home_path"
fi
# 统一设置临时密码
echo "TempPass123" | passwd --stdin "$username" &>/dev/null
((counter++))
done
三、并发执行优化
在进行网络探测等耗时操作时,串行执行效率较低。可以通过后台运行(&)结合 wait 实现并发。
#!/bin/bash
network_range="192.168.1"
alive_list="/tmp/hosts_alive.log"
down_list="/tmp/hosts_down.log"
# 清空旧日志
> "$alive_list"
> "$down_list"
for (( ip=1; ip<=50; ip++ )); do
(
ping -c 1 -W 1 "${network_range}.${ip}" &>/dev/null
if [[ $? -eq 0 ]]; then
echo "${network_range}.${ip} UP" >> "$alive_list"
else
echo "${network_range}.${ip} DOWN" >> "$down_list"
fi
) &
done
# 等待所有后台任务完成
wait
echo "扫描结束。存活主机:$(wc -l < "$alive_list")"
四、多路分支选择(Case Statement)
case 结构用于匹配变量值与多个模式,适合处理菜单选项或服务控制脚本。
基本语法
case $variable in
pattern1)
# 动作组 1
;;
pattern2)
# 动作组 2
;;
*)
# 默认动作
;;
esac
服务管理脚本示例
模拟一个简易的服务守护进程启动控制器。
#!/bin/bash
SERVICE_NAME="${1:-default}"
ACTION="${2:-status}"
check_service() {
case "$ACTION" in
start)
systemctl start "$SERVICE_NAME" && echo "Service started." || echo "Start failed."
;;
stop)
systemctl stop "$SERVICE_NAME" && echo "Service stopped." || echo "Stop failed."
;;
restart)
systemctl restart "$SERVICE_NAME" && echo "Service restarted." || echo "Restart failed."
;;
status)
systemctl is-active "$SERVICE_NAME"
;;
*)
echo "Unknown action: $ACTION. Try: start, stop, restart, status"
exit 1
;;
esac
}
if [ -z "$SERVICE_NAME" ]; then
echo "Usage: $0 <action>"
exit 1
fi
check_service
交互式维护菜单
集成 while 循环以支持多次交互。
#!/bin/bash
show_menu() {
echo "=== System Monitor Menu ==="
echo "C - CPU Usage"
echo "M - Memory Info"
echo "D - Disk Space"
echo "Q - Quit Program"
echo "========================"
}
while true; do
show_menu
read -p "Enter choice: " option
case "$option" in
c|C)
top -bn1 | head -n 5
;;
m|M)
free -h
;;
d|D)
df -h
;;
q|Q)
echo "Exiting..."
break
;;
*)
echo "Invalid selection."
;;
esac
echo "--- Press Enter to continue ---"
read
done
五、嵌套逻辑综合应用
在处理复杂业务逻辑时,往往需要在循环内部进行条件判断,或在分支中包含循环。
质数判定优化
无需遍历所有数字,只需检查至平方根即可。
#!/bin/bash
read -p "Enter a positive integer: " num
if (( num < 2 )); then
echo "$num is not a prime number."
exit 0
fi
is_prime=1
# 仅遍历到开方值的整数部分
max_check=$(echo "sqrt($num)" | bc)
for (( k=2; k<=max_check; k++ )); do
if (( num % k == 0 )); then
is_prime=0
break
fi
done
if (( is_prime == 1 )); then
echo "$num is a Prime Number."
else
echo "$num is Not a Prime Number."
fi
闰年逻辑校验
能被 4 整除且不能被 100 整除,或者能被 400 整除。
#!/bin/bash
read -p "Check Year: " year
if (( year % 400 == 0 )) || (( year % 4 == 0 && year % 100 != 0 )); then
echo "$year is a Leap Year."
else
echo "$year is a Common Year."
fi