【网络运维】Shell 脚本编程:while 循环与 until 循环

Shell 脚本编程:while 循环与 until 循环

循环结构简介

循环语句是 Shell 脚本中用于重复执行一条或一组指令的重要工具,直到满足特定条件时停止执行。Shell 脚本中常见的循环语句包括 while、until、for 和 select。本文将重点介绍 while 和 until 两种循环结构,并通过丰富的示例展示其实际应用场景。

while 循环与 until 循环语法解析

while 循环语法

while 循环属于"当型"循环结构,其基本语法格式为:

bash 复制代码
while <条件表达式>
do
  指令...
done

执行逻辑

  • 首先判断条件表达式是否成立
  • 如果成立,则执行循环体内的指令
  • 每次执行到 done 时重新判断条件表达式
  • 直到条件不成立时退出循环

until 循环语法

until 循环属于"直到型"循环结构,其基本语法格式为:

bash 复制代码
until <条件表达式>
do
  指令...
done

执行逻辑

  • 当条件表达式不成立时执行循环体
  • 直到条件表达式成立时终止循环

基础应用示例

示例1:竖向打印数字

while 实现方式

bash 复制代码
#!/bin/bash
i=5
while ((i>0))  # 当i大于0时执行循环
do
  echo $i     # 打印当前i值
  ((i--))     # i自减1
done

until 实现方式

bash 复制代码
#!/bin/bash
i=5
until ((i==0))  # 直到i等于0时停止循环
do
  echo $i       # 打印当前i值
  ((i--))       # i自减1
done

示例2:计算1-100的累加和

bash 复制代码
#!/bin/bash
i=1
sum=0
while ((i<=100))    # 当i小于等于100时执行循环
do
  ((sum+=i))        # 累加i到sum变量
  # let sum=sum+i   # 另一种累加方式
  ((i++))           # i自增1
  # let i++         # 另一种自增方式
done
echo "1+2+3+...+99+100=$sum"  # 输出结果

示例3:计算5的阶乘

bash 复制代码
#!/bin/bash
i=1
sum=1
while ((i<=5))    # 循环5次
do
  ((sum*=i))      # 累乘计算阶乘
  ((i++))         # i自增1
done
echo "5的阶乘为:$sum"  # 输出结果

示例4:猴子吃桃问题

问题描述

  • 猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个
  • 第二天早上又将第一天剩下的桃子吃掉一半,又多吃了一个
  • 以后每天早上都吃了前一天剩下的一半零一个
  • 到第10天早上想再吃时,发现只剩下一个桃子了

问:猴子第一天摘了多少个桃子?

while循环解法

bash 复制代码
#!/bin/bash
# 当天桃子数量,第10天为1
today=1
# 前一天桃子数量
lastday=0
# 只需要迭代9次(从第10天倒推回第1天)
i=1
while ((i<=9))
do
  # 计算上一天桃子数量:today = (lastday/2) - 1 → lastday = (today+1)*2
  lastday=$[(today+1)*2]
  # 把上一天的数量当作今天的数量,继续向前推算
  today=${lastday}
  ((i++))
done
echo "猴子第一天摘的桃子数量是:$today。"

函数递归解法

bash 复制代码
#!/bin/bash
function sum (){
  if [[ $1 = 1 ]];then
    echo $1  # 第10天只剩1个桃子
  else
    # 递归计算:第n天的桃子数 = (第n+1天的桃子数 + 1) * 2
    echo $[ ($(sum $[$1 -1]) + 1)*2 ]
  fi
}
echo "猴子第一天摘的桃子数量是:$(sum 10)。"

示例5:猜数字游戏

bash 复制代码
#!/bin/bash
# 生成1-50的随机数字
random_num=$[ RANDOM%50+1 ]
echo "${random_num}" >> /tmp/number  # 保存随机数(用于调试)

# 记录猜测次数
i=0
while true  # 无限循环,直到猜对退出
do
  read -p "猜一猜系统产生的50以内随机数是:" num
  if ((num>=1 && num<=50));then  # 验证输入有效性
    ((i++))  # 增加猜测次数
    if [ $num -eq ${random_num} ];then
      echo "恭喜你,第$i次猜对了!"
      rm -f /tmp/number  # 清理临时文件
      exit  # 退出脚本
    else
      echo -n "第$i次猜测,加油。"
      # 提供大小提示
      [ $num -gt ${random_num} ] && echo "太大了,往小猜。" || echo "太小了,往大猜。"
    fi
  else
    echo "请输入一个介于1-50之间的数字。"
  fi 
done

脚本后台运行与管理

后台运行方法

在实际工作中,可能需要让脚本在后台持续运行:

  1. 使用 & 符号sh /server/scripts/while_01.sh &
  2. 使用 nohup 命令nohup /server/scripts/uptime.sh &
  3. 使用 screen 会话screen -S session_name 然后执行脚本

进程管理命令

  • sh whilel.sh &:后台运行脚本
  • ctl+c:停止当前任务
  • ctl+z:暂停当前任务
  • bg:将任务放到后台运行
  • fg:将任务调到前台运行
  • jobs:查看当前任务
  • kill:终止指定任务

并发控制示例

示例:让所有CPU满负荷工作

bash 复制代码
#!/bin/bash
# 获取CPU核心数量
cpu_count=$(lscpu|grep '^CPU(s)'|awk '{print $2}')
i=1
while ((i<=${cpu_count}))
do
  {
   while :  # 无限循环
   do
     ((1+1))  # 简单计算消耗CPU
   done
   } &  # 放到后台运行
 ((i++))
done

注意事项

  • { command1; command2; ... } & 可以将多个命令放到后台运行
  • {} 内部两侧需要有空格
  • 最后一个命令后需要有分号

使用wait等待后台任务完成

bash 复制代码
#!/bin/bash
> /tmp/sleep  # 清空文件
i=1
while [ $i -le 10 ]
do
  # 每个任务睡眠i秒后写入文件
  ( sleep $i && echo sleep $i >> /tmp/sleep )&
  ((i++))
done
wait  # 等待所有后台任务完成
cat /tmp/sleep  # 显示结果

实战应用

示例1:监控系统负载

bash 复制代码
#!/bin/bash
while true  # 无限循环
do
  uptime    # 显示系统负载
  sleep 2   # 休眠2秒
done

后台运行并记录日志

bash 复制代码
#!/bin/bash
while true
do
  uptime >> /tmp/loadaverage.log  # 追加到日志文件
  sleep 2
done

# 后台运行
bash while2.sh &

示例2:服务监控与自动重启

while格式

bash 复制代码
#!/bin/bash
while true
do 
  # 检查sshd服务是否活跃
  systemctl is-active sshd.service &>/dev/null
  if [ $? -ne 0 ];then  # 如果服务不活跃
    systemctl restart sshd.service  &>/dev/null  # 重启服务
  fi
  sleep 5  # 每5秒检查一次
done

until格式

bash 复制代码
#!/bin/bash
until false  # 一直执行直到false(永远不会发生)
do 
  systemctl is-active sshd.service &>/dev/null
  if [ $? -ne 0 ];then 
    systemctl restart sshd.service  &>/dev/null
  fi
  sleep 5
done

示例3:网站可用性监控

bash 复制代码
#!/bin/bash

# 参数检查
if [ $# -ne 1 ];then
  echo "Usage: $0 url"
  exit 1
fi

url="$1"

while true
do
  # 使用curl检查网站可用性
  if curl -o /dev/null -s --connect-timeout 5 $url;then
    echo "$(date): $url is ok."  # 添加时间戳
  else
    echo "$(date): $url is error."
  fi
  sleep 3  # 每3秒检查一次
done

示例4:简易短信平台模拟

bash 复制代码
#!/bin/bash

# 初始化变量
money=0.5  # 默认金额
msg_file=/tmp/message  # 消息保存文件
> $msg_file  # 清空消息文件

# 手机操作菜单
function print_menu () {
  cat << EOF
1. 查询余额
2. 发送消息
3. 充值
4. 退出
EOF
}

# 数字检查函数
function check_digit () {
  expr $1 + 1 &> /dev/null && return 0 || return 1
}

# 显示余额函数
function check_money_all () {
    echo "余额为:$money 元。"
}

# 检查余额是否充足(每条短信0.15元)
function check_money () {
  # 将元转换为分进行比较
  new_money=$(echo "$money*100"|bc|cut -d . -f1)
  if [ ${new_money} -lt 15 ];then
    echo "余额不足,请充值。"
    return 1  # 余额不足
  else
    return 0  # 余额充足
  fi
}

# 充值函数
function chongzhi () {
  read -p "充值金额(单位:元):" chongzhi_money
  while true
  do
    check_digit $chongzhi_money
    if [ $? -eq 0 ] && [ ${chongzhi_money} -ge 1 ];then
      money=$( echo "($money+${chongzhi_money})"|bc)  # 使用bc进行浮点计算
      echo "当前余额为:$money 元"
      return 0
    else
      read -p "重新输入充值金额(至少1元):" chongzhi_money 
    fi
  done
}

# 发送消息函数
function send_msg () {
  check_money  # 检查余额
  if [ $? -eq 0 ];then  # 余额充足
    read -p "请输入消息内容:" message
    echo "$(date): $message" >> ${msg_file}  # 保存消息带时间戳

    # 计算新余额(每条消息0.15元)
    new_money=$(echo "scale=2;($money*100-15)" | bc |cut -d. -f1)
    if [ ${new_money} -ge 100 ];then
      money=$(echo "scale=2;${new_money}/100" | bc )
    else
      money=0$(echo "scale=2;${new_money}/100" | bc )
    fi
    echo "消息已发送,当前余额为:$money 元"
  fi
}

# 主程序
while true
do
  print_menu
  echo
  read -p "请输入你的选择:" choice
  clear
  case $choice in
    1)
      check_money_all
      ;;
    2)
      send_msg
      ;;
    3)
      chongzhi
      ;;
    4)
      echo "感谢使用,再见!"
      exit
      ;;
    *)
      echo "无效选择,请从1、2、3、4中选择。" 
      ;;
  esac
  echo
done

while循环读取文件的四种方式

以读取 /etc/hosts 文件为例:

方式1:使用exec重定向

bash 复制代码
#!/bin/bash
exec < /etc/hosts  # 将文件重定向到标准输入
while read line
do
  echo $line
done

方式2:使用管道

bash 复制代码
#!/bin/bash
cat /etc/hosts | while read line
do
  echo $line
done

方式3:使用输入重定向

bash 复制代码
#!/bin/bash
while read line
do
  echo $line
done < /etc/hosts

方式4:设置IFS分隔符

bash 复制代码
#!/bin/bash
IFS=$'\n'  # 设置字段分隔符为换行符
for line in $(cat /etc/hosts)
do
  echo $line
done

实战案例

案例1:防止DDoS攻击 - Web日志分析

bash 复制代码
#!/bin/bash
logfile=$1  # 日志文件路径参数

while true
do
  # 提取IP并统计访问次数
  awk '{print $1}' $logfile | grep -v "^$" | sort | uniq -c > /tmp/tmp.log
  
  # 处理统计结果
  exec < /tmp/tmp.log
  while read line
  do
    ip=$(echo $line | awk '{print $2}')  # 提取IP
    count=$(echo $line | awk '{print $1}')  # 提取访问次数
    
    # 如果访问次数超过500且不在防火墙黑名单中
    if [ $count -gt 500 ] && [ $(iptables -L -n | grep "$ip" | wc -l) -lt 1 ];then
      iptables -I INPUT -s $ip -j DROP  # 封禁IP
      echo "$(date): $ip is dropped (PV: $count)" >> /tmp/droplist_$(date +%F).log
    fi
  done
  
  sleep 3600  # 每小时检查一次
done

案例2:防止DDoS攻击 - 网络连接数监控

bash 复制代码
#!/bin/bash
while true
do
  # 统计ESTABLISHED状态的连接并按IP分组
  ss -t | grep ESTAB | awk '{print $4}' | cut -d: -f1 | sort | uniq -c > /tmp/tmp.log
  
  exec < /tmp/tmp.log
  while read line
  do
    ip=$(echo $line | awk '{print $2}')
    count=$(echo $line | awk '{print $1}')
    
    # 如果单个IP连接数超过100且未被封禁
    if [ $count -gt 100 ] && [ $(iptables -L -n | grep "$ip" | wc -l) -lt 1 ];then
      iptables -I INPUT -s $ip -j DROP
      echo "$(date): $ip is dropped (连接数: $count)" >> /tmp/droplist_$(date +%F).log
    fi
  done
  
  sleep 10  # 每10秒检查一次
done

总结

  1. while循环特点

    • 擅长执行守护进程和持续运行的应用
    • 适合处理频率小于1分钟的循环任务
    • 多数while循环可用for循环或cron定时任务替代
  2. 各语句使用场景

    • 条件表达式:简短条件判断(文件存在、字符串非空等)
    • if语句:不同值数量较少的条件判断
    • for循环:常规循环处理的首选
    • while循环:守护进程、无限循环(需配合sleep控制频率)
    • case语句:服务启动脚本、固定规则字符串处理
    • select语句:菜单打印(较少使用,通常用here文档替代)
  3. 函数的作用

    • 使代码逻辑更加清晰
    • 减少重复代码开发
    • 提高代码可维护性
相关推荐
国科安芯23 分钟前
高速CANFD收发器ASM1042在割草机器人轮毂电机通信系统中的适配性研究
网络·单片机·嵌入式硬件·性能优化·机器人·硬件工程
晓梦.1 小时前
IPSec 安全基础
服务器·网络·安全
btyzadt3 小时前
虚拟机蓝屏问题排查与解决
linux·运维·网络
佩佩(@ 。 @)4 小时前
网络编程-创建TCP协议服务器
服务器·网络·tcp/ip
张鱼小丸子4 小时前
MySQL企业级部署与高可用实战
运维·数据库·mysql·云原生·高可用·mha·组从复制
coderklaus4 小时前
Shell 基础知识
linux·macos·shell
争不过朝夕,又念着往昔5 小时前
即时通讯项目---网关服务
linux·c++·vscode
时空自由民.5 小时前
linux下camera 详细驱动流程 OV02K10为例(chatgpt版本)
linux·运维·服务器
码界奇点5 小时前
Python内置函数全解析:30个核心函数语法、案例与最佳实践指南
linux·服务器·python