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

【网络运维】Shell 脚本编程: for 循环与 select 循环

Shell 脚本编程: for 循环与 select 循环

循环语句命令常用于重复执行一条指令或一组指令,直到条件不再满足时停止,Shell脚本语言的循环语句常见的有while、until、for及select循环语句。

本文将详细介绍Shell编程中for循环和select循环的各种应用场景和实践技巧。

for 循环语句介绍

for 循环是 Shell 编程中常用的循环结构,主要用于执行次数有限的循环任务。与 while 循环不同,for 循环更适合处理已知迭代次数的场景。for 循环主要有两种语法结构:变量取值型C语言型

变量取值型 for 循环

语法结构:

for 变量名 in 变量取值列表
do指令...
done

说明:

  • in 变量取值列表可以省略,省略时相当于 in "$@"
  • 执行过程:变量名依次获取取值列表中的每个值,执行循环体内的指令

执行流程:

  1. 变量获取列表中的第一个值
  2. 执行循环体内的指令
  3. 执行到 done 后结束本次循环
  4. 变量获取列表中的下一个值,重复上述过程
  5. 直到取完列表中所有值

C 语言型 for 循环

语法结构:

for ((exp1; exp2; exp3))
do指令...
done

说明:

  • exp1:变量初始化(如:i=0)
  • exp2:循环条件(如:i<100)
  • exp3:变量更新(如:i++)

执行流程:

  1. 执行初始化表达式
  2. 检查条件表达式,如果为真则进入循环
  3. 执行循环体内的指令
  4. 执行变量更新表达式
  5. 重复步骤2-4,直到条件表达式为假

for 循环基础实践示例

示例1:竖向升序打印数字

#!/bin/bash
# 列表型for循环
for i in {1..5}
doecho $i
done# 使用seq命令
for i in $(seq 5)
doecho $i
done# C语言型for循环
for ((i=1; i<=5; i++))
doecho $i
done# while循环实现相同功能
i=1
while ((i<=5)) 
doecho $i((i++))
done

示例2:竖向降序打印数字

#!/bin/bash
# 使用序列表达式
for i in {5..1}
doecho $i
done# 使用seq命令带步长
for i in $(seq 5 -1 1)
doecho $i
done

示例3:求和与求乘积

#!/bin/bash
# 求1-10的和
sum=0
for i in {1..10}
dosum=$[ sum + i ]  # 累加计算
done
echo $(seq -s '+' 10)=$sum  # 显示计算过程# 求1-10的乘积
sum=1
for i in {1..10}
do((sum *= i))  # 累乘计算
done
echo $(seq -s '*' 10)=$sum  # 显示计算过程

示例4:求水仙花数

水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身。

#!/bin/bash
for num in {100..999}
don1=$[num/100]        # 获取百位数n2=$[num%100/10]     # 获取十位数n3=$[num%10]         # 获取个位数# 判断是否为水仙花数if [ $[ n1**3 + n2**3 + n3**3 ] -eq $num ];thenecho $n1^3 + $n2^3 + $n3^3 = $n1$n2$n3fi
done

示例5:求素数

素数又称质数,是指除了 1 和它本身以外,不能被任何整数整除的数。

#!/bin/bash
echo -n "100~200之间所有的素数为:"
for ((i=100; i<=200; i++))
do# 除数的范围是2和本身-1的数for ((j=2; j<=i-1; j++))do# 如果能整除,说明不是质数,跳出本次循环if [ $[i%j] -eq 0 ];thenbreakfi# 如果本次循环到最后都不能整除,则是质数if [ $j -eq $[i-1] ];thenecho -n " $i"fidone
done
echo

执行结果:

100~200之间所有的素数为: 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199

示例6:九九乘法表

#!/bin/bash
for num1 in {1..9}
dofor ((num2=1; num2<=num1; num2++))do# 格式化输出,使用制表符对齐echo -en "$num2*$num1=$[ num2 * num1 ]\t"doneecho  # 换行
done

示例7:求出满足条件的4位数

在这里插入图片描述

解题思路:
在这里插入图片描述

  • 余数是5,除数肯定大于5,至少是6
  • 大于等于6的个位数乘以一个数,结果还是一位数,则商上面的每个数都是1

解法1:从1000开始枚举到9999

#!/bin/bash
# num是被除数
for num in {1000..9999}
do# i是除数for i in {6..9}do# 分解数字的各位num1=$[$num/1000]       # 千位num2=$[$num/100%10]     # 百位num3=$[$num/10%10]      # 十位num4=$[$num%10]         # 个位e=$[num1*10+num2-i]     # 第一次计算if ((e<10));thenf=$[e*10+num3-i]      # 第二次计算if ((f<10 && $[f*10+num4-i]==5));thenecho "${i}x111+5=$num"  # 输出结果fifidone
done

解法2:分别对每个数进行枚举

#!/bin/bash
for ((i=6; i<=9; i++))      # 除数范围
dofor ((a=1; a<=9; a++))    # 千位数dofor ((b=0; b<=9; b++))  # 百位数doe=$[a*10+b-i]         # 第一次计算# e是个位数if [ $e -lt 10 ];thenfor ((c=0; c<=9; c++))  # 十位数do# f是个位数f=$[e*10+c-i]         # 第二次计算if [ $f -lt 10 ];thenfor ((d=0; d<=9; d++))  # 个位数doif [ $[f*10+d-i] -eq 5 ];then  # 最终余数应为5echo "${i}x111+5=$[i*111+5]"  # 输出结果fidonefidonefidonedone
done

性能对比:

$ time bash compute1.sh
9x111+5=1004
real	0m0.986s$ time bash compute2.sh
9x111+5=1004
real	0m0.014s

**思考:**compute2.sh时间更短是因为它通过数学分析减少了循环次数,直接从可能的除数开始枚举,避免了不必要的计算。

示例8:百钱买百鸡

中国古代数学家张丘建在他的《算经》中提出了著名的"百钱买百鸡问题":

  • 鸡翁一,值钱五
  • 鸡母一,值钱三
  • 鸡雏三,值钱一
  • 百钱买百鸡,问翁、母、雏各几何?
#!/bin/bash
# 鸡翁数量 cock_num
# 鸡母数量 hen_num
# 鸡雏数量 chick_num
# 鸡总数量 sum
sum=100
# 钱总数 money
money=100for ((cock_num=1; cock_num<=money/5; cock_num++))
dofor ((hen_num=1; hen_num<=money/3; hen_num++))dofor chick_num in {3..100..3}  # 鸡雏数是3的倍数do# 判断数量和金额是否都满足条件if [ $[ cock_num+hen_num+chick_num ] -eq $sum -a \$[ cock_num*5+hen_num*3+chick_num/3 ] -eq $money ];thenecho "鸡翁:$cock_num  鸡母:$hen_num  鸡雏:$chick_num"echo "$cock_num*5+$hen_num*3+$chick_num/3=100"echofidonedone
done

执行结果:

鸡翁:4  鸡母:18  鸡雏:78
4*5+18*3+78/3=100鸡翁:8  鸡母:11  鸡雏:81
8*5+11*3+81/3=100鸡翁:12  鸡母:4  鸡雏:84
12*5+4*3+84/3=100

示例9:三对情侣参加婚礼

3个新郎为A、B、C,3个新娘为X、Y、Z。得到的假话信息:

  • A说他将和X结婚
  • X说她的未婚夫是C
  • C说他将和Z结婚

解题思路:

  • 根据假话得出反向判定条件
  • 多个人不能同时和一个人结婚

解法1:直接枚举

#!/bin/bash
for A in X Y Z
dofor B in X Y Zdofor C in X Y Zdo# 判断条件:所有人不能重复,且与假话相反if [ "$A" != "$B" -a "$A" != "$C" -a "$B" != "$C" -a \"$A" != "X" -a "$C" != "X" -a "$C" != "Z" ];thenecho "A 与 $A 结婚"echo "B 与 $B 结婚"echo "C 与 $C 结婚"fidonedone
done

解法2:使用数字表示,更高效

#!/bin/bash
# 将X Y Z 抽象为 1 2 3
# 定义函数,根据参数值返回对应字母
duixiang () {case $1 in1) echo X ;;2) echo Y ;;3) echo Z ;;esac
}for ((A=1; A<=3; A++))
dofor ((B=1; B<=3; B++))dofor ((C=1; C<=3; C++))do# 多个人不能同时和一个人结婚# 根据假话得出反向判定条件if [ $A -ne 1 -a $C -ne 1 -a $C -ne 3 -a \$A -ne $B -a $A -ne $C -a $B -ne $C ];thenecho "A 与 $(duixiang $A) 结婚"echo "B 与 $(duixiang $B) 结婚"echo "C 与 $(duixiang $C) 结婚"exitfidonedone
done

执行结果:

A 与 Z 结婚
B 与 X 结婚
C 与 Y 结婚

示例10:求a+aa+aaa+…aaaaa

例如:2+22+222+2222+22222(此时共有5个数相加),数字和数字最长位数由键盘输入。

#!/bin/bash
read -p "输入1-9之间一个数:" m
read -p "输入数字的最长位数:" n# 定义最终和
sum=0for ((i=1; i<=n; i++))
do# count是j位m的值,每次计算完count值设置为0count=0for ((j=1; j<=i; j++))do# 计算j位m的值count=$[count + m * 10**(j-1)]doneecho -n "$count "# 控制输出格式if [ $i -eq $n ];thenecho -n "= "elseecho -n "+ "fisum=$[sum + count]
done
echo $sum

执行结果:

输入1-9之间一个数:6
输入数字的最长位数:5
6 + 66 + 666 + 6666 + 66666 = 74070

示例11:排列组合

有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

#!/bin/bash
# 组合数量为sum
sum=0echo -n "组合包括:"
for ((i=1; i<=4; i++))
dofor ((j=1; j<=4; j++))dofor ((k=1; k<=4; k++))do# 确保三位数互不相同if [ $i -ne $j -a $i -ne $k -a $j -ne $k ];thenecho -n "$i$j$k "sum=$[sum+1]fidonedone
done
echo
echo "组合总数量为:$sum"

执行结果:

组合包括:123 124 132 134 142 143 213 214 231 234 241 243 312 314 321 324 341 342 412 413 421 423 431 432 
组合总数量为:24

for 循环语句实践案例

示例1:批量修改文件扩展名

# 创建测试文件
touch file-{1..3}.txt# 方法1:使用sed获取新文件名
#!/bin/bash
for file in $(ls *.txt)
donew_name=$(echo $file | sed 's/.txt$/.jpg/')  # 替换扩展名mv $file ${new_name}  # 重命名文件
done# 方法2:使用rename命令(更高效)
rename ".jpg" ".txt" *.jpg  # 批量修改扩展名
rename "file-" "" *.txt     # 删除文件名中的特定字符串

示例2:批量设置服务开机启动

#!/bin/bash
for service in crond atd chronyd 
dosystemctl enable $service  # 设置服务开机启动
done

示例3:批量重启多个主机sshd服务

#!/bin/bash
for host in server{1..10}
dossh root@$host systemctl restart sshd  # 远程重启服务
done

示例4:批量备份文件

#!/bin/bash
for file in host*
docp $file /backup/${file}.bak  # 备份文件并添加.bak后缀
done

示例5:扫描网络中在线主机

#!/bin/sh
for i in `seq 1 254`
doIP=172.25.254.$ping -c2 $IP &> /dev/null  # 发送2个ping包if [ $? -eq 0 ];thenecho "$IP is online" >> node_online.txtelse            echo "$IP is offline" >> node_offline.txtfi  
done# 注意:添加&符号可实现并发扫描

示例6:监控主机磁盘使用情况

#!/bin/bash
hostfile=/tmp/hosts
if [ $(cat $hostfile | wc -l) -lt 1 ];thenecho $hostfile is empty.exit 1
fifor host in $(cat $hostfile)
do> disk.log  # 清空日志文件echo "=============HOSTNAME: $host =============" >> disk.log# 获取磁盘使用率超过80%的分区ssh $host df -h | grep '^/dev/' | awk '{print $1,$5}' | \cut -f 1 -d% | awk '{if ($2 > 80) print $0}' >> disk.log# 如果有超过80%的分区,发送邮件报警if [ $(cat disk.log | wc -l) -gt 1 ];thencat disk.log | mail -s "$host: disk is greater than 80%" root@localhostfi
done

for 循环语句高级实战案例

示例1:MySQL数据库备份

# 创建数据库
#!/bin/bash
MYSQL_CMD='mysql -uroot -e '
for db in web1 web2
do${MYSQL_CMD} "create database $db;"  # 创建数据库
done# 备份数据库
#!/bin/bash
MYSQL_CMD='mysqldump -uroot '
for db in web1 web2
do${MYSQL_CMD} $db > $db.sql  # 备份数据库到文件
done

示例2:MySQL分库分表备份

# 创建表
#!/bin/bash
MYSQL_CMD='mysql -uroot $db -e '
for db in web1 web2
do# 创建表并插入测试数据${MYSQL_CMD} "create table test(id int,name varchar(16));insert into test values(1,'testdata')";
done# 备份表
#!/bin/bash
MYSQL_CMD='mysqldump -uroot $db'
for db in web1 web2
do${MYSQL_CMD} > ${db}_test.sql  # 备份表数据
done

示例3:批量创建系统账号

#!/bin/bash
# 引入系统函数库
. /etc/init.d/functionsusers_info_file=/etc/users
users_list_file=/tmp/users[ ! -f $users_info_file ] && touch $users_info_fileexec < ${users_list_file}  # 重定向输入
# 创建用户并设置密码
while read username
do# 生成随机密码(使用md5加密并截取)pass=$(echo "$user+$RANDOM" | md5sum | cut -c 3-11)# 添加用户并设置密码useradd $username &>/dev/null && chage -d 0 $username && \echo "$pass" | passwd --stdin $username &>/dev/null && \echo -e "username: $username \t password:$pass" >> $users_info_file# 根据返回值判断用户和密码是否添加成功if [ $? -eq 0 ];thenaction "$username is ok" /bin/true  # 优雅显示成功elseaction "$username is fail" /bin/false  # 优雅显示失败fi
doneecho "--------------------------"
cat $users_info_file  # 显示创建的用户信息

Linux 系统产生随机数的6种方法

方法1:通过系统环境变量 RANDOM

echo "furongwang$RANDOM" | md5sum | cut -c 1-8  # 结合md5增强随机性

方法2:通过openssl产生随机数

openssl rand -base64 8    # 生成8位随机Base64字符串
openssl rand -base64 20   # 生成20位随机Base64字符串

方法3:通过date命令获得随机数

date +%s%N  # 输出纳秒时间戳

方法4:通过设备文件生成随机数

head /dev/urandom | md5sum   # 使用随机设备生成随机数
head /dev/random | md5sum    # 使用高质量随机设备

方法5:通过UUID生成随机数

cat /proc/sys/kernel/random/uuid  # 读取UUID
uuidgen                           # 生成UUID

方法6:使用expect附带的mkpasswd命令

mkpasswd -l 20 -d 4 -c 4 -C 4 -s 4  # 生成符合复杂度要求的密码

方法7:使用mktemp命令和sha1sum

mktemp -u | sha1sum  # 创建临时文件路径并计算哈希值

select 循环语句介绍及语法

select循环主要用于创建交互式菜单,其语法结构如下:

select 变量名 [ in 菜单取值列表 ]
do指令...
done

特点:

  • in 菜单取值列表可省略,省略时相当于使用 in "$@"
  • 执行后会显示数字序号菜单,等待用户选择
  • 用户输入数字序号后执行相应循环体
  • 默认提示符为#?,可通过PS3环境变量自定义
  • 用户选择的序号保存在REPLY环境变量中
  • select是无限循环,需要显式退出

select 循环语句案例

示例1:打印课程清单

#!/bin/bash
select course in mysql python linux
doecho $course  # 输出选择的课程
done

示例2:更改提示符并配合case使用

#!/bin/bash
PS3="选择你的课程: "  # 自定义选择提示符
select course in linux mysql python exit
doecho "你选择了第 $REPLY 个条目。"  # REPLY保存用户选择的序号case $course inlinux)echo "linux xxx";;mysql)echo "mysql yyy";;python)echo "python zzz";;exit)exit  # 退出循环;;esacecho
done

示例3:两个数计算器

#!/bin/bash
num1=10
num2=20
echo "There are two numbers $num2 and $num1."select method in 加法 减法 乘法 退出
docase $method in加法)echo $[num1 + num2]  # 加法运算;;减法)echo $[num2 - num1]  # 减法运算;;乘法)echo $[num1 * num2]  # 乘法运算;;退出)exit  # 退出程序;;*)echo "从1-4中选择"  # 错误输入提示;;esac
done

示例4:一键安装mysql和httpd

#!/bin/bash
# 定义输出颜色函数
function print () {if [ "$1" == "PASS" ];thenecho -e '\033[1;32mPASS\033[0;39m'    # 绿色elif [ "$1" == "FAIL" ];thenecho -e '\033[1;31mFAIL\033[0;39m'    # 红色elif [ "$1" == "DONE" ];thenecho -e '\033[1;35mDONE\033[0;39m'    # 紫色elseecho "Usage: print PASS|FAIL|DONE"fi
}# 定义服务安装函数
function InstallService () {case $1 inhttpd)echo -n "Start installing httpd..."{ yum install -y httpd                # 安装httpdsystemctl enable httpd --now        # 设置开机启动并立即启动echo "Hello World" > /var/www/html/index.html  # 创建测试页面} &>/dev/null  # 屏蔽输出[ $? -eq 0 ] && print DONE || print FAIL  # 判断执行结果;;mysql)echo -n "Start installing mysql..."{ yum install -y mariadb-server       # 安装mariadbsystemctl enable mariadb --now      # 设置开机启动并立即启动} &>/dev/null  # 屏蔽输出[ $? -eq 0 ] && print DONE || print FAIL  # 判断执行结果;;esac
}# 主菜单
select app in "Install httpd" "Install mysql" "Exit"
docase $REPLY in1)InstallService httpd  # 安装httpd;;2)InstallService mysql  # 安装mysql;;3)exit  # 退出程序;;*)echo "从1-3中选择。"  # 错误输入提示;;esac
done

总结

for循环的核心要点:

  1. 两种语法结构:变量取值型和C语言型,各有适用场景
  2. 擅长处理已知迭代次数的循环任务
  3. 可以嵌套使用解决复杂问题
  4. 在企业环境中常用于批量操作、监控和自动化任务

select循环的核心要点:

  1. 专为创建交互式菜单设计
  2. 配合case语句可以实现复杂的交互逻辑
  3. 通过PS3和REPLY环境变量自定义提示符和获取用户选择
  4. 适合编写用户友好的命令行工具
http://www.dtcms.com/a/348148.html

相关文章:

  • ARINC 825板卡的应用
  • vue-pure-admin页面引入和功能添加流程解析
  • Smooze Pro for mac 鼠标手势增强软件
  • 力扣【1277. 统计全为1的正方形子矩阵】——从暴力到最优的思考过程
  • 商超客流密度统计误差率↓35%!陌讯多模态融合算法在零售智慧运营的实战解析
  • 智慧零售商品识别误报率↓74%!陌讯多模态融合算法在自助结算场景的落地优化
  • Ubuntu24.04 安装 Zabbix
  • 使用UE5开发2.5D开放世界战略养成类游戏的硬件配置指南
  • IDM 下载失败排查指南:全面解析与解决方案
  • 马斯克宣布开源Grok 2.5:非商业许可引争议,模型需8×40GB GPU运行,Grok 3半年后开源
  • Redis实战-缓存的解决方案(一)
  • 【贪心算法】day1
  • 【数学建模】灰色关联分析的核心步骤
  • 上位机知识篇---电脑参数
  • Shell脚本-影响shell程序的内置命令
  • [机械结构设计-32]:机械加工中,3D图评审OK,没有问题,后续的主要风险有哪些
  • Bright Data MCP:突破AI数据获取限制的革命性工具
  • M8504报错,开票数量大于收货数量
  • 请求上下文对象RequestContextHolder
  • 【datawhale组队学习】RAG技术 - TASK04 向量及多模态嵌入(第三章1、2节)
  • AI Agent全栈开发流程推荐(全栈开发步骤)
  • 在 vue3 和 vue2 中,v-for 和 v-if 可以一起用吗,区别是什么
  • Win10部署ElasticSearch、Logstash、Kibana
  • wpf之Grid控件
  • 图像均衡化详解:从直方图均衡到 CLAHE,让图片告别 “灰蒙蒙“
  • 征程 6X 常用工具介绍
  • 第16届蓝桥杯C++中高级选拔赛(STEMA)2024年12月22日真题
  • elasticsearch 7.x elasticsearch 使用scroll滚动查询中超时问题案例
  • 【C#】构造函数实用场景总结
  • PostgreSQL interval 转换为 int4 (整数)