shell 编程之循环语句
目录
一、for 循环语句
二、while 循环语句
三、until 循环语句
四、总结扩展
1. 循环对比
2. 调试技巧
3. 易混淆点解析
4. 进阶技巧
一、for 循环语句
1. 基础概念
含义:
用于 遍历一个已知的列表,逐个执行同一组命令
核心作用:
对一组固定或可枚举的数据(如文件列表、数字序列)进行重复操作。
结构:
for 变量 in 取值列表 do 命令组 done
2. 示例
根据姓名列表批量添加用户
1.准备测试文件(user_list.txt)
2.添加编辑脚本
#!/bin/bash
# 定义用户列表文件路径
USER_LIST="/root/user_list.txt"
# 检查文件是否存在
if [ ! -f "$USER_LIST" ]; then
echo "错误:用户列表文件 $USER_LIST 不存在!"
exit 1
fi
# 使用 for 循环遍历文件中的用户名
for username in $(cat "$USER_LIST"); do
# 跳过空行和注释行(以 # 开头)
if [[ -z "$username" || "$username" == \#* ]]; then
continue
fi
# 检查用户是否已存在
if id "$username" &> /dev/null; then
echo " 用户 $username 已存在,跳过..."
else
# 创建用户并设置家目录
useradd -m "$username"
# 设置初始密码(示例密码:123456)
echo "123456" | passwd --stdin $username &>/dev/null
###stdin : 标准输入 ,默认用键盘输入
echo "用户 $username 创建成功!"
fi
done
echo "所有用户处理完成!"
批量重启服务
#!/bin/bash
# 检查是否以 root 用户身份运行
if [ "$(id -u)" -ne 0 ]; then
echo "请以 root 用户身份运行此脚本。"
exit 1
fi
# 定义要重启的服务列表
services=("nginx" "mysql" "redis")
log_file="/var/log/service_restart.log"
# for 循环:批量重启服务
for service in "${services[@]}"; do
# 检查服务是否正在运行
if systemctl is-active --quiet $service; then
echo "正在重启 $service 服务..."
systemctl restart $service
if [ $? -eq 0 ]; then
echo "$service 服务重启成功。"
else
echo "$service 服务重启失败。" | tee -a $log_file
systemctl status $service >> $log_file 2>&1
echo "---------------------" >> $log_file
fi
else
echo "$service 服务未运行,无需重启。"
fi
done
3. 注意事项
-
列表元素包含空格时需加引号:
for item in "file 1" "file 2"
-
使用
$@
遍历脚本参数:for param in "$@"
二、while 循环语句
1. 基础概念
含义:
当 条件测试结果为真(true)时,重复执行循环体,直到条件变为假(false)。
核心作用:
处理不确定循环次数的场景(如持续读取输入、等待某个状态变化)。
结构:
while [ 条件测试 ] do 命令组 done
2. 示例
批量添加规律编号的用户
#!/bin/bash
PREFIX="stu"
#定义整数变量值为1
i=1
#设置密码为123456的批量用户
while [ $i -le 20 ]
do
useradd ${PREFIX}$i
echo "123456" | passwd --stdin ${PREFIX}$i &> /dev/null
let i++
done
echo "所有用户处理完成!"
持续监控磁盘使用率
#!/bin/bash
# 设定磁盘使用率阈值
threshold=80
# 日志文件路径
log_file="/var/log/disk_usage.log"
# while 循环:持续监控磁盘使用率
while true; do
usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $usage -gt $threshold ]; then
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
message="$timestamp - 磁盘使用率超过 $threshold%,当前使用率为 $usage%。"
echo "$message" | tee -a $log_file
# 可以添加发送邮件或者其他告警逻辑
fi
sleep 3600 # 每小时检查一次
done
3. 注意事项
-
避免死循环:若条件永远为真(如
while true
),需手动退出(break) -
管道输入会创建子shell:
echo "文本" | while read
(子shell变量不传递到主进程)
三、until 循环语句
1. 基础概念
含义:
当 条件测试结果为假(false)时,重复执行循环体,直到条件变为真(true)。
核心作用:
与 while
逻辑相反,适用于需要“等待某个条件达成”的场景。
结构:
until [ 条件测试 ] # 条件为假时执行 do 命令组 done
2. 示例
指定用户发送在线消息
#!/bin/bash
# 检查参数数量是否足够
if [ $# -lt 2 ]; then
echo "Usage: $0 <username1> <username2> ... <message>"
exit 0
fi
# 提取消息,消息为最后一个参数
message="${!#}"
# 遍历除最后一个参数外的所有用户名
for username in "${@:1:$#-1}"; do
# 检查用户是否为系统内用户
if ! grep -q "^$username:" /etc/passwd; then
echo "$(date +'%Y-%m-%d %H:%M:%S') - $username is not a valid user on this system."
continue
fi
# 持续检查用户是否登录
while ! who | grep -q "$username"; do
echo "$(date +'%Y-%m-%d %H:%M:%S') - $username is not logged on. Waiting for the user to log in."
sleep 60
done
# 检查用户是否允许接收消息
if ! mesg -c | grep -q "is y"; then
echo "$(date +'%Y-%m-%d %H:%M:%S') - $username has disabled message reception."
continue
fi
# 用户已登录,发送消息
echo "$(date +'%Y-%m-%d %H:%M:%S') - Sending message to $username..."
write "$username" <<EOF
$message
EOF
if [ $? -ne 0 ]; then
echo "$(date +'%Y-%m-%d %H:%M:%S') - Failed to send message to $username."
fi
done
持续尝试连接数据库,直至连接成功
#!/bin/bash
# 数据库连接信息
host="localhost"
port="3306"
user="root"
password="password"
# until 循环:持续尝试连接数据库直到成功
until mysql -h $host -P $port -u $user -p$password -e "SELECT 1" &> /dev/null; do
echo "无法连接到数据库,等待 10 秒后重试..."
sleep 10
done
echo "成功连接到数据库。"
3. 特点
-
与
while
逻辑相反:条件为假时持续执行 -
适合「等待型」场景(如检测服务就绪)
四、总结扩展
1. 循环对比
循环类型 | 执行逻辑 | 典型场景 |
---|---|---|
for | 遍历列表中的每个元素 | 处理文件、已知范围的数值迭代 |
while | 条件为真时执行 | 读取输入流、条件动态变化的场景 |
until | 条件为假时执行 | 等待某条件满足(如服务启动) |
2. 调试技巧
#!/bin/bash -x # 开启调试模式查看循环执行细节 for file in * do : # 空语句用于占位调试 done
3. 易混淆点解析
-
while
vsuntil
:-
while
:条件为真时执行 → “只要...就继续” -
until
:条件为假时执行 → “直到...才停止”
-
-
for
vswhile
:-
for
关注 遍历数据,while
关注 动态条件。
-
4. 进阶技巧
-
嵌套循环:
for i in {1..3} do while [ $j -lt 2 ] do echo "$i-$j" ((j++)) done done
-
循环控制:
-
break
:立即退出循环。 -
continue
:跳过当前迭代,进入下一轮。
-
-
性能优化:
-
避免在循环内重复执行相同命令(如
ls
),优先缓存结果。 -
使用
&
后台执行耗时任务(需权衡并发控制) -
避免在循环内频繁调用
grep/awk
等外部命令 -
大数据处理优先使用管道和xargs
-
删除旧日志:
#!/bin/bash # 清理 30 天前的日志文件(处理特殊字符) LOG_DIR="/var/log/app" DAYS=30 find "$LOG_DIR" -type f -name "*.log" -mtime +$DAYS -print0 | while IFS= read -r -d '' file; do echo "删除旧日志: $file" rm -f "$file" done
注释说明:
-
-mtime +30
:匹配修改时间超过 30 天的文件
-print0
+read -d ''
:安全处理含空格/换行符的文件名
-
避免频繁启动子进程:
# 低效写法:每次循环调用一次 `date` for i in {1..100}; do current_time=$(date) echo "$current_time" done # 高效写法:预先获取时间 current_time=$(date) for i in {1..100}; do echo "$current_time" done
总结速查表
场景 推荐循环 关键技巧 遍历文件列表 for
+ 引号包裹变量处理空格: find + while read -r
动态读取输入/条件变化 while
用 break
/continue
控制流程等待条件达成(如超时) until
结合 sleep
避免 CPU 占用过高高性能批量处理 xargs
/parallel
并行加速,减少子进程开销 无限循环 while true
确保有退出条件(如 Ctrl+C
捕获信号)