当前位置: 首页 > news >正文

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 vs until

    • while:条件为真时执行 → “只要...就继续”

    • until:条件为假时执行 → “直到...才停止”

  • for vs while

    • 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 捕获信号)

 

相关文章:

  • UNet深度学习实战遥感图像语义分割
  • 孟加拉slot游戏出海代投FB脸书广告策略
  • HTTP协议入门
  • c# 委托和事件的区别及联系,Action<T1,T2>与Func<T1,T2>的区别
  • RTX 5060 Ti 3DMark跑分首次流出:比RTX 4060 Ti快20%
  • JVM——运行时数据区
  • Linux内核中struct net_protocol的early_demux字段解析
  • 谷歌A2A与Anthropic MCP: AI 智能体互补双协议
  • 【MySQL】MVCC工作原理、事务隔离机制、undo log回滚日志、间隙锁
  • mac中的zip文件压缩与压缩文件中指定目录删除
  • 【话题讨论】Python + AI图像生成实战:AI图像生成——用代码点亮数字艺术
  • Uipath获取最新下载文件
  • Express学习笔记(六)——前后端的身份认证
  • 嵌入式基础(三)基础外设
  • 论文阅读笔记——Generating Long Sequences with Sparse Transformers
  • Before After:SQL整容级优化
  • 学习八股的随机思考
  • Scratch037-(钢琴)
  • 数据库9(实验过程中补充学习)
  • 负氧离子是怎样产生的?
  • 五一出境游火爆:境外包车订单增长25%,日本酒店价格贵了好几倍
  • 新华社经济随笔:把握不确定性中的确定性
  • 对话|听老婆的话,UFC“下山虎”张名扬的铁汉柔情
  • 牛市早报|李强:在一些关键的时间窗口,推动各方面政策措施早出手、快出手
  • 经济日报经世言:不断开创中马关系发展新局面
  • 美国佛罗里达州立大学枪击事件已致2人死亡