流程控制
基础知识
编程语言的目的是通过风格化的编程思路将代码写出来后,实现项目功能的。为了实现功能,我们在代码层面通过一些代码逻辑来实现:
- 顺序执行 - 程序按从上到下顺序执行
- 选择执行 - 程序执行过程中,根据条件选择不同的顺序执行
- 循环执行 - 程序执行过程中,根据条件重复执行代码
在shell编程中,默认情况下,处于shell脚本中的命令,它是按照从上到下的方式顺序执行每一条命令,这也导致我们在shell编程的过程中,必须保证每一条命令都能够正常的执行。当然了,真实的生产中的shell编程,不可能仅有这一种编程逻辑
许多程序在脚本命令之间需要某种逻辑流控制,这就意味着shell脚本在具体的场景中,根据条件判断选择一条具体的代码逻辑执行特定范围的命令--脚本范围内,允许出现多个场景下的命令块,而控制执行不同命令块的编程逻辑结构,在shell编程中有一个名称--结构化命令
结构化命令允许脚本程序根据条件或者相关命令的结果进行判断,执行一些功能命令块,在另外一些条件下,执行跳过这些命令块。在shell编程中,这些结构化的命令主要有:
- 条件逻辑 - 多分支执行命令块
- if控制语句
- case控制语句
- select控制语句
- 循环逻辑 - 多循环执行命令块
- for控制语句
- while控制语句
- until控制语句
- 逻辑控制 - 命令块执行过程中,精细化控制
- continue控制
- break控制
- exit控制
- shift控制
if条件控制
语法解读
条件结构能够根据某个特定的条件,结合内置的测试表达式功能,结合测试的结果状态值对于条件进行判断,然后选择执行合适的任务。在bash中,if命令是条件结构最简单的形式。shell中的if语句支持多种条件的决策形式:
单路决策 - 单分支if语句 样式: if [ 条件 ] then 指令 fi 特点: 单一条件,只有一个输出
双路决策 - 双分支if语句 样式: if [ 条件 ] then 指令1 else 指令2 fi 特点: 单一条件,两个输出
多路决策 - 多分支if语句 样式: if [ 条件 ] then 指令1 elif [ 条件2 ] then 指令2 else 指令3 fi 特点: n个条件,n+1个输出
单行命令写法 if [ 条件1 ]; then 指令1; elif [ 条件2 ]; then 指令2; ... ; else 指令n; fi
关键点解读:if 和 then 配套使用,if 和末尾的 fi 顺序反写
内嵌测试语句,shell的if语句中关于条件判断这块内嵌了如下几种测试表达式语句:
- [ 表达式 ] 针对通用的判断场景
- [[ 表达式 ]] 针对扩展的判断场景
- (( 命令 )) (())代替let命令来测试数值表达式
简单实践
实践1-单if实践
single_branch_if.sh
#!/bin/bash
# 单分支if语句的使用场景
# 定制普通变量
gender="$1"
# 条件判断逻辑
if [ "${gender}" == "nan" ]
then
echo "您的性别是 男"
fi
/bin/bash single_branch_if.sh nv
/bin/bash single_branch_if.sh nan
您的性别是 男
实践2-双if实践
double_branch_if.sh
#!/bin/bash
# 双分支if语句的使用场景
# 定制普通变量
gender="$1"
# 条件判断逻辑
if [ "${gender}" == "nan" ]
then
echo "您的性别是 男"
else
echo "您的性别是 女"
fi
/bin/bash double_branch_if.sh
您的性别是 女
/bin/bash double_branch_if.sh nan
您的性别是 男
/bin/bash double_branch_if.sh xxx
您的性别是 女
实践3-多if实践
multi_branch_if.sh
#!/bin/bash
# 多分支if语句的使用场景
# 定制普通变量
gender="$1"
# 条件判断逻辑
if [ "${gender}" == "nan" ]
then
echo "您的性别是 男"
elif [ "${gender}" == "nv" ]
then
echo "您的性别是 女"
else
echo "您的性别,我不知道"
fi
/bin/bash multi_branch_if.sh
您的性别,我不知道
/bin/bash multi_branch_if.sh nan
您的性别是 男
/bin/bash multi_branch_if.sh nv
您的性别是 女
/bin/bash multi_branch_if.sh xxx
您的性别,我不知道
if 案例实践
服务管理
案例需求
要求脚本执行需要有参数,通过传入参数来实现不同的功能
参数和功能详情如下: 参数 执行效果 start 服务启动中... stop 服务关闭中... restart 服务重启中...
脚本 X.sh 使用方式 /bin/bash X.sh [ start|stop|restart ]
service_manager_if.sh
#!/bin/bash
# 功能:定制服务管理的功能
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通变量
service_ops="$1"
# 脚本基本判断
if [ $# -ne 1 ]
then
echo -e "\e[31m$0 脚本的使用方式: $0 [ start | stop | restart ]\e[0m"
exit
fi
# 脚本内容的判断
if [ "${service_ops}" == "start" ]
then
echo -e "\e[31m服务启动中...\e[0m"
elif [ "${service_ops}" == "stop" ]
then
echo -e "\e[31m服务关闭中...\e[0m"
elif [ "${service_ops}" == "restart" ]
then
echo -e "\e[31m服务重启中...\e[0m"
else
echo -e "\e[31m$0 脚本的使用方式: $0 [ start | stop | restart ]\e[0m"
fi
/bin/bash service_manager_if.sh
service_manager_if.sh 脚本的使用方式: service_manager_if.sh [ start | stop | restart ]
/bin/bash service_manager_if.sh start
服务启动中...
/bin/bash service_manager_if.sh stop
服务关闭中...
/bin/bash service_manager_if.sh restart
服务重启中...
/bin/bash service_manager_if.sh xxx
service_manager_if.sh 脚本的使用方式: service_manager_if.sh [ start | stop | restart ]
堡垒机登录
需求:在之前的堡垒机功能基础上,扩充条件判断效果
simple_jumpserver_if.sh
#!/bin/bash
# 功能:定制堡垒机的展示页面
# 版本:v0.4
# 作者:example
# 联系:www.example.com
# 定制普通变量
login_user='root'
login_pass='123456'
# 堡垒机的信息提示
echo -e "\e[31m \t\t 欢迎使用堡垒机"
echo -e "\e[32m-----------请选择你要登录的远程主机-----------
1: 10.0.0.14 (nginx)
2: 10.0.0.15 (tomcat)
3: 10.0.0.19 (apache)
q: 使用本地主机
----------------------------------------------\033[0m"
# 由于暂时没有学习条件判断,所以暂时选择 q
read -p "请输入您要选择的远程主机编号: " host_index
read -p "请输入登录本地主机的用户名: " user
read -s -p "请输入登录本地主机的密码: " password
echo
# 远程连接主机
if [[ ${user} == ${login_user} && ${password} == ${login_pass} ]]
then
echo -e "\e[31m主机登录验证成功\e[0m"
else
echo -e "\e[31m您输入的用户名或密码有误\e[0m"
fi
/bin/bash simple_jumpserver_if.sh
欢迎使用堡垒机
-----------请选择你要登录的远程主机-----------
1: 10.0.0.14 (nginx)
2: 10.0.0.15 (tomcat)
3: 10.0.0.19 (apache)
q: 使用本地主机
----------------------------------------------
请输入您要选择的远程主机编号: q
请输入登录本地主机的用户名: root
请输入登录本地主机的密码:
主机登录验证成功
/bin/bash simple_jumpserver_if.sh
欢迎使用堡垒机
-----------请选择你要登录的远程主机-----------
1: 10.0.0.14 (nginx)
2: 10.0.0.15 (tomcat)
3: 10.0.0.19 (apache)
q: 使用本地主机
----------------------------------------------
请输入您要选择的远程主机编号: q
请输入登录本地主机的用户名: zhang
请输入登录本地主机的密码:
您输入的用户名或密码有误
嵌套if实践
一个if语句仅仅能够针对一个场景的多种情况。当我们面对多场景的条件判断的时候,一个if结构语句无法满足需求,这个时候,我们可以借助于嵌套if的结构语句来实现相应的效果
注意:如果是多个独立的、彼此没有关联的业务场景,可以使用不同的if语句即可。嵌套的if语句只能适用于彼此关联的业务场景
简单实践
案例需求
运维管理人员,通过监控平台获取站点的运行状态数据信息,当发现问题的时候,根据情况进行后续判断: 状况1: 问题类型 研发标识 - 交给开发团队 测试标识 - 交给测试团队 运维标识 - 交给运维团队 状况2: 问题级别 红灯 - 紧急故障 黄灯 - 严重故障 绿灯 - 一般故障 灰灯 - 未知故障,后续操作
monitor_operator_if.sh
#!/bin/bash
# 功能:定制监控异常的处理措施
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通变量
monitor_type=(研发 测试 运维)
error_level=(红灯 黄灯 绿灯 灰灯)
# 监控平台的信息提示
echo -e "\e[31m \t\t 欢迎使用监控处理平台"
echo -e "\e[32m-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------\033[0m"
# 定制业务逻辑
read -p "请输入问题标识: " monitor_id
# 判断问题类型是否有效,monitor_type数组的长度不能小于monitor_id
if [ ${#monitor_type[@]} -lt ${monitor_id} ]
then
echo -e "\e[31m无效标识,请输入正确的问题标识\e[0m"
else
# 定制问题类型识别逻辑
if [ ${monitor_type[$monitor_id-1]} == "研发" ]
then
echo -e "\e[31m转交研发团队处理\e[0m"
elif [ ${monitor_type[$monitor_id-1]} == "测试" ]
then
echo -e "\e[31m转交测试团队处理\e[0m"
elif [ ${monitor_type[$monitor_id-1]} == "运维" ]
then
echo -e "\e[32m-----------请选择故障级别-----------"
echo " 1: 红灯 2: 黄灯 3: 绿灯 4: 灰灯"
echo -e "----------------------------------\033[0m"
read -p "请输入故障级别: " level_id
# 判断故障级别是否有效,error_level数组的长度不能小于level_id
if [ ${#error_level[@]} -lt ${level_id} ]
then
echo -e "\e[31m无效标识,请输入正确的故障级别\e[0m"
else
# 定制故障级别逻辑
if [ ${error_level[$level_id-1]} == "红灯" ]
then
echo -e "\e[31m请按照 紧急故障 性质进行处理\e[0m"
elif [ ${error_level[$level_id-1]} == "黄灯" ]
then
echo -e "\e[31m请按照 严重故障 性质进行处理\e[0m"
elif [ ${error_level[$level_id-1]} == "绿灯" ]
then
echo -e "\e[31m请按照 一般故障 性质进行处理\e[0m"
elif [ ${error_level[$level_id-1]} == "灰灯" ]
then
echo -e "\e[31m请按照 未知故障 性质进行处理\e[0m"
fi
fi
fi
fi
/bin/bash monitor_operator_if.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 4
无效标识,请输入正确的问题标识
/bin/bash monitor_operator_if.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 1
转交研发团队处理
/bin/bash monitor_operator_if.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 3
-----------请选择故障级别-----------
1: 红灯 2: 黄灯 3: 绿灯 4: 灰灯
----------------------------------
请输入故障级别: 1
请按照 紧急故障 性质进行处理
/bin/bash monitor_operator_if.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 3
-----------请选择故障级别-----------
1: 红灯 2: 黄灯 3: 绿灯 4: 灰灯
----------------------------------
请输入故障级别: 5
无效标识,请输入正确的故障级别
其他实践
if条件控制语句支持(())来代替let命令测试表达式的执行效果,支持[[]]实现正则级别的内容匹配
表达式的样式如下: if (( 表达式 )) 或 [[ 内容匹配 ]] then 指令 fi
实践1-(()) 计算条件匹配
odd_even_if.sh
#!/bin/bash
# (()) 在if分支中的应用
# 接收一个数字
read -p "请输入一个数字: " num
# 定制数字奇数和偶数判断
if (( ${num} % 2 == 0 ))
then
echo -e "\e[31m ${num} 是一个偶数\e[0m"
else
echo -e "\e[31m ${num} 是一个奇数\e[0m"
fi
/bin/bash odd_even_if.sh
请输入一个数字: 2
2 是一个偶数
/bin/bash odd_even_if.sh
请输入一个数字: 1
1 是一个奇数
实践2-[[]]扩展匹配条件
condition_extend_if.sh
#!/bin/bash
# [[]] 在if分支中的应用
# 接收一个数字
read -p "请输入一个单词: " string
# 判断内容是否是我们想要的
if [[ ${string} == v* ]]
then
echo -e "\e[31m ${num} 是满足条件的单词\e[0m"
else
echo -e "\e[31m ${num} 不满足条件\e[0m"
fi
/bin/bash condition_extend_if.sh
请输入一个单词: very
是满足条件的单词
/bin/bash condition_extend_if.sh
请输入一个单词: hello
不满足条件
实践3-[[]]扩展实践
person_height_if.sh
#!/bin/bash
# [[]] 在if分支中的应用
# 接收一个数字
read -p "请输入你的身高(m为单位): " height
# 身高判断逻辑
if [[ ! ${height} =~ ^[0-2](\.[0-9]{,2})?$ ]]
then
echo -e "\e[31m你确定自己的身高是 ${height} ?\e[0m"
else
echo -e "\e[31m我说嘛,${height} 才是你的身高!\e[0m"
fi
/bin/bash person_height_if.sh
请输入你的身高(m为单位): 3
你确定自己的身高是 3 ?
/bin/bash person_height_if.sh
请输入你的身高(m为单位): 1.2
我说嘛,1.2 才是你的身高!
单行命令
所谓的单行命令,其实指的是对于简单的if条件判断,我们可以直接一行显示,减少代码的行数
# 示例1-命令行的if语句
num1=3 num2=1
if [ $num1 -gt $num2 ]; then echo "$num1 数字大"; else echo "$num2 数字大"; fi
3 数字大
num1=3 num2=9
if [ $num1 -gt $num2 ]; then echo "$num1 数字大"; else echo "$num2 数字大"; fi
9 数字大
# 示例2-使用逻辑表达式来满足if效果
num1=3 num2=1
[ $num1 -gt $num2 ] && echo "$num1 数字大" || echo "$num2 数字大"
3 数字大
num1=3 num2=9
[ $num1 -gt $num2 ] && echo "$num1 数字大" || echo "$num2 数字大"
9 数字大
case条件控制
语法解读
case命令是一个多路分支的命令,它可以来代替 if/elif相关的命令,在case语句中,它通过引入一个变量接收用户输入的数据,然后依次与相关的值进行匹配判断,一旦找到对应的匹配值后,就执行相关的语句
语法格式
case 变量名 in 值1) 指令1 ;; ... 值n) 指令n ;; esac
注意:首行关键字是case,末行关键字esac;选择项后面都有 );每个选择的执行语句结尾都有两个分号
简单实践
案例需求
改造多分支if语句对脚本进行升级 要求脚本执行需要有参数,通过传入参数来实现不同的功能。
参数和功能详情如下: 参数 执行效果 start 服务启动中... stop 服务关闭中... restart 服务重启中...
脚本 X.sh 使用方式 X.sh [ start|stop|restart ]
service_manager_case.sh
#!/bin/bash
# 功能:定制服务管理的功能
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通变量
service_ops="$1"
# 脚本内容的判断
case "${service_ops}" in
"start")
echo -e "\e[31m服务启动中...\e[0m";;
"stop")
echo -e "\e[31m服务关闭中...\e[0m";;
"restart")
echo -e "\e[31m服务重启中...\e[0m";;
*)
echo -e "\e[31m$0 脚本的使用方式: $0 [ start | stop | restart ]\e[0m";;
esac
/bin/bash service_manager_case.sh
service_manager_case.sh 脚本的使用方式: service_manager_case.sh [ start | stop | restart ]
/bin/bash service_manager_case.sh start
服务启动中...
/bin/bash service_manager_case.sh stop
服务关闭中...
/bin/bash service_manager_case.sh restart
服务重启中...
/bin/bash service_manager_case.sh xxx
service_manager_case.sh 脚本的使用方式: service_manager_case.sh [ start | stop | restart ]
case实践
环境标准化
案例需求
由于项目环境复杂,虽然我们创建了大量的脚本,但是管理起来不方便,需要我们做一个简单的脚本执行管理平台,输入不同的环境关键字,罗列该环境下的相关脚本
信息提示 欢迎使用脚本管理平台 -----------请选择功能场景----------- 1: 系统环境下脚本 2: web环境下脚本 3: 数据库环境下脚本 4: 存储环境下脚本 5: 其他环境下脚本
# 准备工作
mkdir os web sql storage other
touch {os/os-{1..3}.sh,web/web-{1..4}.sh,sql/sql-{1..5}.sh,storage/st-{1..2}.sh,other/other.sh}
scripts_manager_case.sh
#!/bin/bash
# 功能:定制服务管理的功能
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制数组变量
env_array=(os web sql storage other)
# 监控平台的信息提示
echo -e "\e[31m 欢迎使用脚本管理平台"
echo -e "\e[32m-----------请选择功能场景-----------
1: 系统环境下脚本
2: web环境下脚本
3: 数据库环境下脚本
4: 存储环境下脚本
5: 其他环境下脚本
----------------------------------\033[0m"
# 定制业务逻辑
read -p "请输入功能标识: " env_id
# 脚本内容的判断
case "${env_array[$env_id-1]}" in
"os")
echo -e "\e[31m系统环境下脚本文件有:\e[0m"
ls os;;
"web")
echo -e "\e[31mweb环境下脚本文件有:\e[0m"
ls web;;
"sql")
echo -e "\e[31m数据库环境下脚本文件有:\e[0m"
ls sql;;
"storage")
echo -e "\e[31m存储环境下脚本文件有:\e[0m"
ls storage;;
"other")
echo -e "\e[31m其他环境下脚本文件有:\e[0m"
ls other;;
*)
echo -e "\e[31m请输入有效的功能场景标识\e[0m";;
esac
/bin/bash scripts_manager_case.sh
欢迎使用脚本管理平台
-----------请选择功能场景-----------
1: 系统环境下脚本
2: web环境下脚本
3: 数据库环境下脚本
4: 存储环境下脚本
5: 其他环境下脚本
----------------------------------
请输入功能标识: 6
请输入有效的功能场景标识
/bin/bash scripts_manager_case.sh
欢迎使用脚本管理平台
-----------请选择功能场景-----------
1: 系统环境下脚本
2: web环境下脚本
3: 数据库环境下脚本
4: 存储环境下脚本
5: 其他环境下脚本
----------------------------------
请输入功能标识: 3
数据库环境下脚本文件有:
sql-1.sh sql-2.sh sql-3.sh sql-4.sh sql-5.sh
k8s部署
案例需求
由于k8s项目环境复杂,它依赖于很多功能步骤操作,我们需要按照合理的步骤部署整体环境。相关的操作步骤信息如下
基础环境部署 跨主机免密码操作 时间同步操作 内核配置操作 容器私有仓库操作 高可用环境部署 高可用环境部署 负载均衡环境部署 kubernetes基础环境部署 证书管理 etcd环境部署 集群证书配置 主角色环境部署 apiserver环境部署 scheduler环境部署 controller环境部署 认证配置 容器环境部署 kubelet环境部署 kube-proxy环境部署 从角色环境部署 容器环境部署 kubelet环境部署 kube-proxy环境部署
kubernetes_manager_case.sh
#!/bin/bash
# 功能:定制kubernetes环境部署管理的功能
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制数组变量
env_array=(base ha k8s_base master slave)
# 监控平台的信息提示
echo -e "\e[31m 欢迎使用kubernetes部署平台"
echo -e "\e[32m-----------请选择部署阶段-----------
1: 基础环境部署
2: 高可用环境部署
3: kubernetes基础环境部署
4: 主角色环境部署
5: 从角色环境部署
----------------------------------\033[0m"
# 定制业务逻辑
read -p "请输入功能标识: " env_id
# 脚本内容的判断
case "${env_array[$env_id-1]}" in
"base")
echo -e "\e[31m开始基础环境部署..."
echo "1 执行跨主机免密码操作"
echo "2 执行时间同步操作"
echo "3 执行内核配置操作"
echo -e "4 执行容器私有仓库部署操作\e[0m";;
"ha")
echo -e "\e[31高可用环境部署..."
echo "1 执行高可用环境部署操作"
echo -e "2 执行负载均衡环境部署操作\e[0m";;
"k8s_base")
echo -e "\e[31mkubernetes基础环境部署..."
echo "1 执行证书管理操作"
echo "2 执行etcd环境部署操作"
echo -e "3 执行集群证书配置操作\e[0m";;
"master")
echo -e "\e[31m主角色环境部署..."
echo "1 执行apiserver环境部署操作"
echo "2 执行scheduler环境部署操作"
echo "3 执行controller环境部署操作"
echo "4 执行认证配置操作"
echo "5 执行容器环境部署操作"
echo "6 执行kubelet环境部署操作"
echo -e "7 执行kube-proxy环境部署\e[0m";;
"slave")
echo -e "\e[31m主角色环境部署..."
echo "1 执行容器环境部署操作"
echo "2 执行kubelet环境部署操作"
echo -e "3 执行kube-proxy环境部署\e[0m";;
*)
echo -e "\e[31m请输入有效的功能场景标识\e[0m";;
esac
/bin/bash kubernetes_manager_case.sh
欢迎使用kubernetes部署平台
-----------请选择部署阶段-----------
1: 基础环境部署
2: 高可用环境部署
3: kubernetes基础环境部署
4: 主角色环境部署
5: 从角色环境部署
----------------------------------
请输入功能标识: 7
请输入有效的功能场景标识
/bin/bash kubernetes_manager_case.sh
欢迎使用kubernetes部署平台
-----------请选择部署阶段-----------
1: 基础环境部署
2: 高可用环境部署
3: kubernetes基础环境部署
4: 主角色环境部署
5: 从角色环境部署
----------------------------------
请输入功能标识: 5
主角色环境部署...
1 执行容器环境部署操作
2 执行kubelet环境部署操作
3 执行kube-proxy环境部署
嵌套实践
这里的嵌套实践,与if语句的嵌套实践,基本一致,只不过组合的方式发生了一些变化。常见的组合样式如下:
case嵌套if语句 case "变量" in "值1") if [ 条件判断 ] ... ;; ... esac
case嵌套case语句 case "变量" in "值1") case语句 ... esac
if嵌套case语句 if [ 条件判断 ] then case "变量名" in "值1") 指令1;; ... esac else ... fi
简单实践
场景简介: 火车站安检 安检失败 - 携带违禁物品禁止进站 安检成功后进行火车票检查 - 允许登上上火车 - 禁止登上火车
实践1-case嵌套if语句
case_if.sh
#!/bin/bash
# 功能:case嵌套if语句实践
# 定制业务逻辑
read -p "是否携带违禁物品: " safe_check
# 业务逻辑定制
case "${safe_check}" in
"true")
echo -e "\e[31m不允许进入火车站\e[0m";;
"false")
read -p "火车票是否过期: " ticket_check
if [ ${ticket_check} == "true" ]
then
echo -e "\e[31m不允许登上火车站\e[0m"
else
echo -e "\e[31m允许登上火车站\e[0m"
fi;;
*)
echo -e "\e[31m再检查一遍\e[0m";;
esac
/bin/bash case_if.sh
是否携带违禁物品: true
不允许进入火车站
/bin/bash case_if.sh
是否携带违禁物品: false
火车票是否过期: true
不允许登上火车站
/bin/bash case_if.sh
是否携带违禁物品: false
火车票是否过期: false
允许登上火车站
/bin/bash case_if.sh
是否携带违禁物品: haha
再检查一遍
实践2-case嵌套case语句
case_case.sh
#!/bin/bash
# 功能:case嵌套case语句实践
# 定制业务逻辑
read -p "是否携带违禁物品: " safe_check
# 业务逻辑定制
case "${safe_check}" in
"true")
echo -e "\e[31m不允许进入火车站\e[0m";;
"false")
read -p "火车票是否过期: " ticket_check
case ${ticket_check} in
"true")
echo -e "\e[31m不允许登上火车站\e[0m";;
"false")
echo -e "\e[31m允许登上火车站\e[0m";;
*)
echo -e "\e[31m再检查一遍\e[0m";;
esac;;
*)
echo -e "\e[31m再检查一遍\e[0m";;
esac
实践3-if嵌套case语句
if_case.sh
#!/bin/bash
# 功能:if嵌套case语句实践
# 定制业务逻辑
read -p "是否携带违禁物品: " safe_check
# 业务逻辑定制
if [ "${safe_check}" == "true" ]
then
echo -e "\e[31m不允许进入火车站\e[0m"
elif [ "${safe_check}" == "false" ]
then
read -p "火车票是否过期: " ticket_check
case ${ticket_check} in
"true")
echo -e "\e[31m不允许登上火车站\e[0m";;
"false")
echo -e "\e[31m允许登上火车站\e[0m";;
*)
echo -e "\e[31m再检查一遍\e[0m";;
esac
else
echo -e "\e[31m再检查一遍\e[0m"
fi
嵌套案例
案例需求
运维管理人员,通过监控平台获取站点的运行状态数据信息,当发现问题的时候,根据情况进行后续判断: 状况1: 问题类型 研发标识 - 交给开发团队 测试标识 - 交给测试团队 运维标识 - 交给运维团队 状况2: 问题级别 红灯 - 紧急故障 黄灯 - 严重故障 绿灯 - 一般故障 灰灯 - 未知故障,后续操作
改造嵌套if的监控管理脚本
monitor_operator_if_case.sh
# 功能:定制监控异常的处理措施
# 版本:v0.2
# 作者:example
# 联系:www.example.com
# 定制普通变量
monitor_type=(研发 测试 运维)
error_level=(红灯 黄灯 绿灯 灰灯)
# 监控平台的信息提示
echo -e "\e[31m \t\t 欢迎使用监控处理平台"
echo -e "\e[32m-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------\033[0m"
# 定制业务逻辑
read -p "请输入问题标识: " monitor_id
# 判断问题类型是否有效
if [ ${#monitor_type[@]} -lt ${monitor_id} ]
then
echo -e "\e[31m无效标识,请输入正确的问题标识\e[0m"
else
# 定制问题类型识别逻辑
case ${monitor_type[$monitor_id-1]} in
"研发")
echo -e "\e[31m转交研发团队处理\e[0m";;
"测试")
echo -e "\e[31m转交测试团队处理\e[0m";;
"运维")
echo -e "\e[32m-----------请选择故障级别-----------"
echo " 1: 红灯 2: 黄灯 3: 绿灯 4: 灰灯"
echo -e "----------------------------------\033[0m"
read -p "请输入故障级别: " level_id
# 定制故障级别逻辑
case ${error_level[$level_id-1]} in
"红灯")
echo -e "\e[31m请按照 紧急故障 性质进行处理\e[0m";;
"黄灯")
echo -e "\e[31m请按照 严重故障 性质进行处理\e[0m";;
"绿灯")
echo -e "\e[31m请按照 一般故障 性质进行处理\e[0m";;
"灰灯")
echo -e "\e[31m请按照 未知故障 性质进行处理\e[0m";;
*)
echo -e "\e[31m无效标识,请输入正确的故障级别\e[0m";;
esac
esac
fi
/bin/bash monitor_operator_if_case.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 1
转交研发团队处理
/bin/bash monitor_operator_if_case.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 4
无效标识,请输入正确的问题标识
/bin/bash monitor_operator_if_case.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 3
-----------请选择故障级别-----------
1: 红灯 2: 黄灯 3: 绿灯 4: 灰灯
----------------------------------
请输入故障级别: 3
请按照 一般故障 性质进行处理
/bin/bash monitor_operator_if_case.sh
欢迎使用监控处理平台
-----------请选择问题类型-----------
1: 研发 2: 测试 3: 运维
----------------------------------
请输入问题标识: 3
-----------请选择故障级别-----------
1: 红灯 2: 黄灯 3: 绿灯 4: 灰灯
----------------------------------
请输入故障级别: 6
无效标识,请输入正确的故障级别
for循环
for基础
生产工作中,我们有可能会遇到一种场景,需要重复性的执行相同的动作,我们在shell编程的过程中,我们可以借助于循环逻辑的方法来进行处理
循环逻辑语法解析: 关键字 [ 条件 ] do 执行语句 done
注意: 这里的关键字主要有四种: for - 循环遍历一个元素列表 while - 满足条件情况下一直循环下去 until - 不满足条件情况下一直循环下去 select - 一种特殊的循环遍历,侧重于遍历用户输入,一般结合case等语句使用
简单实践
场景:遍历列表 for 值 in 列表 do 执行语句 done
注意: ”for” 循环总是接收 “in” 语句之后的某种类型的元素列表 执行次数和list列表中常数或字符串的个数相同,当循环的数量足够了,就自动退出
列表生成
- 样式1,手工列表,
1 2 3 4 5 6 7
- 样式2,定制列表,
{1..7}
- 样式3,命令生成,
$(seq 1 7)
- 样式4,脚本参数,
$@ $*
实践1-手工列表
for_hand_list.sh
#!/bin/bash
# 功能:手工列表 for循环
for i in yuwen shuxue lishi
do
echo "列表元素: ${i}"
done
/bin/bash for_hand_list.sh
列表元素: yuwen
列表元素: shuxue
列表元素: lishi
实践2-定制列表
for_define_list.sh
#!/bin/bash
# 功能:定制列表 for循环
for i in {1..3}
do
echo "列表元素: ${i}"
done
/bin/bash for_define_list.sh
列表元素: 1
列表元素: 2
列表元素: 3
实践3-命令生成
for_cmd_list.sh
#!/bin/bash
# 功能:命令生成列表 for循环
for i in $(seq 1 3)
do
echo "列表元素: ${i}"
done
/bin/bash for_cmd_list.sh
列表元素: 1
列表元素: 2
列表元素: 3
实践4-脚本参数
for_arg_list.sh
#!/bin/bash
# 功能:脚本参数列表 for循环
for i in $@
do
echo "列表元素: ${i}"
done
/bin/bash for_arg_list.sh 1 2 3
列表元素: 1
列表元素: 2
列表元素: 3
for循环案例
普通循环
所谓的普通循环,仅仅是在特定的范围循环中,对相关命令进行重复性的操作
批量实践1- 批量创建多个使用随机密码的用户
for_add_user.sh
#!/bin/bash
# 功能:for批量创建用户
# 提示:
# /dev/random和/dev/urandom设备文件会生成随机数,第一个依赖系统中断
# 定制普通变量
user_file='/tmp/user.txt'
# 保证文件可用
# 如果文件存在,则清空文件内容
[ -f ${user_file} ] && > ${user_file}
# 定制批量创建用户的业务逻辑
for i in {1..5}
do
# 创建用户
useradd user-$i
# 生成密码
# tr: -d 删除, -c 取反, [:alnum:] 所有字母和数字,tr -dc '[:alnum:]'表示删除所有不是字母和数字
password=$(head /dev/urandom | tr -dc '[:alnum:]' | head -c 8)
# 为用户添加密码
echo ${password} | passwd --stdin user-$i > /dev/null 2>&1
# 信息输出
echo "用户: user-$i, 密码: ${password}" >> ${user_file}
echo -e "\e[31m用户 user-$i 创建成功\e[0m"
done
/bin/bash for_add_user.sh
用户 user-1 创建成功
用户 user-2 创建成功
用户 user-3 创建成功
用户 user-4 创建成功
用户 user-5 创建成功
cat /tmp/user.txt
用户: user-1, 密码: tkmXpK7t
用户: user-2, 密码: PM9KsA7a
用户: user-3, 密码: Sor3Wyxm
用户: user-4, 密码: DBNNtriy
用户: user-5, 密码: I3skWzyj
# 清理环境
for i in {1..5};do userdel -r user-$i;done
批量实践2- 批量对特定网段的主机进行扫描
for_host_check.sh
#!/bin/bash
# 功能:for批量检测网段主机存活情况
# 定制普通变量
netsub='192.168.91'
net_file='/tmp/host.txt'
# 保证文件可用
[ -f ${net_file} ] && > ${net_file}
# 定制批量检测网段主机状态逻辑
for ip in {1..254}
do
# 测试主机连通性
host_status=$(ping -c1 -W1 $netsub.$ip >/dev/null 2>&1 && echo "UP" || echo "DOWN")
echo "$netsub.$ip 主机状态: $host_status" >> ${net_file}
done
# 信息输出
live_num=$(grep UP ${net_file} | wc -l)
unlive_num=$(grep DOWN ${net_file} | wc -l)
echo -e "\e[31m${netsub}.0 网段主机存活情况\e[0m"
echo "------------------------------------"
echo -e "\e[32m${netsub}.0 网段存活主机数量: ${live_num}\e[0m"
echo -e "\e[32m${netsub}.0 网段异常主机数量: ${unlive_num}\e[0m"
/bin/bash for_host_check.sh
192.168.91.0 网段主机存活情况
------------------------------------
192.168.91.0 网段存活主机数量: 2
192.168.91.0 网段异常主机数量: 252
grep UP /tmp/host.txt
192.168.91.2 主机状态: UP
192.168.91.101 主机状态: UP
赋值循环
所谓的赋值循环,指的是在for循环过程中进行数据的统计等相关计算操作
统计实践1- 计算1+2+...+100 的结果
for_odd_sum.sh
#!/bin/bash
# 功能:for统计数据之和
# 定制普通变量
all_sum=0
odd_sum=0
# 定制所有数据求和逻辑
for i in {1..100}
do
let all_sum+=i
done
# 定制所有奇数求和逻辑
for i in {1..100..2}
do
let odd_sum+=i
done
# 信息输出
echo -e "\e[31m所有数据之和: ${all_sum}\e[0m"
echo -e "\e[31m所有奇数之和: ${odd_sum}\e[0m"
/bin/bash for_odd_sum.sh
所有数据之和: 5050
所有奇数之和: 2500
for (())案例
简介
在for循环的语法中,它还支持一种包含赋值+循环双功能的语法,也就是双小括号(()),这种语法的语法格式如下:
- 样式1,单元素样式,
for (( i=0; i<10; i++ ))
- 样式2,多元素样式,
for (( i=0,j=0; i<10; i++,j++ ))
功能解读
- 第一部分定制一个包含初始值的变量名
- 第二部分定制循环的结束条件
- 第三部分定制循环过程中的变量变化效果,为了让循环出现结束
注意事项
- 变量的复制可以包含空格
- 条件中的变量不能用$符号
- 第三部分的数据操作过程不用 expr格式
基础实践
实践1-(())简单使用
# 输出1-5的数字
for ((i=1;i<=5;i++));do echo $i;done
1
2
3
4
5
# 输出1-10中的所有奇数
for ((i=1;i<=10;i+=2));do echo $i;done
1
3
5
7
9
# 输出1-10中的所有偶数
for ((i=2;i<=10;i+=2));do echo $i;done
2
4
6
8
10
实践2-100个数字的求和
for_odd_sum.sh
#!/bin/bash
# 功能:for统计数据之和
# 定制普通变量
all_sum=0
odd_sum=0
# 定制所有数据求和逻辑
for ((i=1;i<=100;i++))
do
let all_sum+=i
done
# 定制所有奇数求和逻辑
for ((i=1;i<=100;i+=2))
do
let odd_sum+=i
done
# 信息输出
echo -e "\e[31m所有数据之和: ${all_sum}\e[0m"
echo -e "\e[31m所有奇数之和: ${odd_sum}\e[0m"
/bin/bash for_odd_sum.sh
所有数据之和: 5050
所有奇数之和: 2500
循环案例
实践1-命令行进度条数字
# %3d%% 指的是 3个数字位置 + 1个%位置,共计4个位置
# 防止信息输出的叠加,采用\e[4D,每次输出信息的时候,光标左移4个位置,信息不会出现叠加
for ((i = 0; i<=100; ++i)); do printf "\e[4D%3d%%" $i;sleep 0.1s; done
100%
# \e[2D 的演示
for ((i = 0; i <= 100; ++i)); do printf "\e[2D%3d%%" $i;sleep 0.1s; done
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 91 9 9 9 9 9 9 9 9100%
实践2-脚本生成进度条
progress_bar_for.sh
#!/bin/bash
# 定制简单的进度条
# 定制进度条的进度符号
str="#"
# 定制进度转动提示符号,注意\\转义
arr=("|" "/" "-" "\\")
# 定制进度条循环控制
for ((i=0; i<=50; i++))
do
# 设定数组信息的变化索引
let index=i%4
# 打印信息,格式:【%s进度符号】【%d进度数字】【%c进度进行中】
# 注意:信息的显示宽度和进度的数字应该适配,否则终端显示不全
# \r表示回车,必须有,否则输出的进度条不会出现叠加
printf "[%-50s][%d%%]%c\r" "$str" "$(($i*2))" "${arr[$index]}"
# 进度的频率
sleep 0.2
# 进度符前进
str+="#"
done
printf "\n"
/bin/bash progress_bar_for.sh
[###################################################][100%]-
实践2-生成10个随机数保存于数组中,并找出其最大值和最小值
compare_nums_for.sh
#!/bin/bash
# 设定随机数比大小
# 设定基本变量
declare -i min max
declare -a nums
# 设定大小比较
for ((i=0;i<10;i++))
do
# 将随机数添加到数组中
nums[$i]=$RANDOM
# 设定初始值
[ $i -eq 0 ] && min=${nums[0]} max=${nums[0]}
# 设定最大值
[ ${nums[$i]} -gt $max ] && max=${nums[$i]}
# 设定最小值
[ ${nums[$i]} -lt $min ] && min=${nums[$i]}
done
echo -e "\e[31m 随机数的统计信息\e[0m"
echo "------------------------------"
echo -e "\e[32m所有的随机数:${nums[@]}"
echo -e "最大的随机数:${max}"
echo -e "最小的随机数:${min}\e[0m"
/bin/bash compare_nums_for.sh
随机数的统计信息
------------------------------
所有的随机数:18744 8756 10608 31272 23776 6856 26998 28375 6280 16851
最大的随机数:31272
最小的随机数:6280
嵌套循环
这里的嵌套实践,与选择语句的嵌套实践基本一致,只不过组合的方式发生了一些变化。常见的组合样式如下:
for嵌套for语句 for 循环列表1 do for 循环列表2 do ... done done
for嵌套if|case语句 for 循环列表 do if 条件判断语句 或 case 条件判断语句 done
if语句嵌套for语句 if 条件判断 then for 循环列表语句 fi
case语句嵌套for语句 case 条件判断 for 循环列表语句 ;; esac
简单实践
for嵌套for语句实践1-输出99乘法表
for_nine_table.sh
#!/bin/bash
# 功能:for打印99乘法表
# 定制打印99乘法表的业务逻辑
# 对第一个位置的数字进行循环
for num1 in {1..9}
do
# 对第二个位置的数字进行循环
for num2 in $(seq $num1)
do
# 信息输出,\t 在输出后面加上制表符 \c 抑制换行符,生成的内容是固定长度
echo -e "\e[$[RANDOM%9+31]m${num1} x ${num2} = $[num1*num2]\e[0m\t\c"
done
echo # 一个子循环一行内容
done
/bin/bash for_nine_table.sh
1 x 1 = 1
2 x 1 = 2 2 x 2 = 4
3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
4 x 1 = 4 4 x 2 = 8 4 x 3 = 12 4 x 4 = 16
5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25
6 x 1 = 6 6 x 2 = 12 6 x 3 = 18 6 x 4 = 24 6 x 5 = 30 6 x 6 = 36
7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 49
8 x 1 = 8 8 x 2 = 16 8 x 3 = 24 8 x 4 = 32 8 x 5 = 40 8 x 6 = 48 8 x 7 = 56 8 x 8 = 64
9 x 1 = 9 9 x 2 = 18 9 x 3 = 27 9 x 4 = 36 9 x 5 = 45 9 x 6 = 54 9 x 7 = 63 9 x 8 = 72 9 x 9 = 81
for嵌套if语句实践2-判断目录中的文件类型
# 准备工作
rm -rf dir
mkdir dir/{server,soft,scripts,logs} -p
touch dir/{{a..c}.sh,{1..3}.txt}
file_type_for_if.sh
#!/bin/bash
# 功能:for嵌套if查看目录下的文件类型
# 定制普通变量
dir_name='dir'
# 获取所有文件列表
for file in $(ls dir)
do
# 判断文件类型
if [ -d ${dir_name}/${file} ]
then
echo -e "\e[31m${dir_name}/${file} 是一个目录文件\e[0m"
elif [ -f ${dir_name}/${file} ]
then
echo -e "\e[31m${dir_name}/${file} 是一个普通文件\e[0m"
fi
done
/bin/bash file_type_for_if.sh
dir/1.txt 是一个普通文件
dir/2.txt 是一个普通文件
dir/3.txt 是一个普通文件
dir/a.sh 是一个普通文件
dir/b.sh 是一个普通文件
dir/c.sh 是一个普通文件
dir/logs 是一个目录文件
dir/scripts 是一个目录文件
dir/server 是一个目录文件
dir/soft 是一个目录文件
# 清理
rm -rf dir
if嵌套for语句实践3-获取系统支持的shell类型
os_shell_if_for.sh
#!/bin/bash
# 功能:if嵌套for查看系统支持的shell类型
# 定制普通变量
shell_file='/etc/shells'
# 获取所有文件列表
if [ -f ${shell_file} ]
then
for shell in $(grep sh /etc/shells)
do
echo -e "\e[31m当前系统支持的shell类型有: ${shell}\e[0m"
done
else
echo -e "\e[31m没有 ${shell_file} 文件\e[0m"
fi
/bin/bash os_shell_if_for.sh
当前系统支持的shell类型有: /bin/sh
当前系统支持的shell类型有: /bin/bash
当前系统支持的shell类型有: /usr/bin/sh
当前系统支持的shell类型有: /usr/bin/bash
当前系统支持的shell类型有: /bin/zsh
综合案例
信息收集
脚本实践-采集系统负载信息,根据提示信息,选择输出 cpu 或者 内存信息
systemctl_load.sh
#!/bin/bash
# 功能:采集系统负载信息
# 版本:v0.2
# 作者:example
# 联系:www.example.com
# 定制资源类型
resource_type=(CPU MEM)
cpu_attribute=(1 5 15)
free_attribute=(总量 使用 空闲)
# 获取相关的属性信息
cpu_load=($(uptime | tr -s " " | cut -d " " -f 11-13 | tr "," " "))
free_info=($(free -m | grep Mem | tr -s " " | cut -d " " -f 2-4))
# 服务的操作提示
echo -e "\e[31m---------------查看资源操作动作---------------
1: CPU 2: MEM
-------------------------------------------"'\033[0m'
# 选择服务操作类型
read -p "> 请输入要查看的资源信息类型: " resource_id
echo
if [ ${resource_type[$resource_id-1]} == "CPU" ]
then
echo -e "\e[31m\t系统CPU负载信息\e[0m"
echo -e "\e[32m================================"
for index in ${!cpu_attribute[@]}
do
echo "CPU ${cpu_attribute[$index]} min平均负载为: ${cpu_load[$index]}"
done
echo -e "================================\e[0m"
elif [ ${resource_type[$resource_id-1]} == "MEM" ]
then
echo -e "\e[31m\t系统内存负载信息\e[0m"
echo -e "\e[32m================================"
for index in ${!free_attribute[@]}
do
echo "内存 ${free_attribute[$index]} 信息为: ${free_info[$index]} M"
done
echo -e "================================\e[0m"
fi
/bin/bash systemctl_load.sh
---------------查看资源操作动作---------------
1: CPU 2: MEM
-------------------------------------------
> 请输入要查看的资源信息类型: 1
系统CPU负载信息
================================
CPU 1 min平均负载为: 0.00
CPU 5 min平均负载为: 0.01
CPU 15 min平均负载为: 0.05
================================
/bin/bash systemctl_load.sh
---------------查看资源操作动作---------------
1: CPU 2: MEM
-------------------------------------------
> 请输入要查看的资源信息类型: 2
系统内存负载信息
================================
内存 总量 信息为: 972 M
内存 使用 信息为: 272 M
内存 空闲 信息为: 102 M
================================
其他实践
需求
按照信息提示,分别打印 三角形 和 等腰梯形
* *****
* * *******
* * * *********
* * * * ***********
* * * * * *************
drawn_graph.sh
#!/bin/bash
# 功能:打印相关图形
# 版本:v0.1
# 作者:example
# 联系:www.example.com
graph_type=(三角形 梯形)
# 服务的操作提示
echo -e "\e[31m---------------查看可以绘制的图形---------------
1: 三角形 2: 梯形
-------------------------------------------"'\033[0m'
# 选择服务操作类型
read -p "> 请输入要查看的资源信息类型: " graph_id
case ${graph_type[$graph_id-1]} in
"三角形")
read -p "> 请输入三角形绘制的层数: " layer_num
# 定制打印n层的三角形
for i in $(seq 1 ${layer_num});do
# 定制打印三角形左侧的空格效果
for m in $(seq $[${layer_num}-$i]);do
echo -n " "
done
# 定制打印三角形核心部分
for j in $(seq $i);do
echo -n "* "
done
# 打印完每行就换行
echo
done;;
"梯形")
read -p "> 请输入梯形绘制的层数: " layer_num
# 定制打印n层的梯形
print_num=${layer_num}
for i in $(seq 1 ${layer_num});do
# 定制打印梯形左侧的空格效果
for m in $(seq $[${layer_num}-$i]);do
echo -n " "
done
# 定制打印梯形核心部分
for j in $(seq $[$print_num]);do
echo -n "*"
done
let print_num+=2
echo
done;;
*)
echo -e "\e[31m\t请输入正确的绘图类型id\e[0m";;
esac
/bin/bash drawn_graph.sh
---------------查看可以绘制的图形---------------
1: 三角形 2: 梯形
-------------------------------------------
> 请输入要查看的资源信息类型: 1
> 请输入三角形绘制的层数: 5
*
* *
* * *
* * * *
* * * * *
/bin/bash drawn_graph.sh
---------------查看可以绘制的图形---------------
1: 三角形 2: 梯形
-------------------------------------------
> 请输入要查看的资源信息类型: 2
> 请输入梯形绘制的层数: 5
*****
*******
*********
***********
*************
while循环
while基础
while命令有点像 if/then 和 for循环之间的结合,while走循环之前会对输入的值进行条件判断,如果满足条件的话,才会进入到循环体中执行对应的语句,否则的话就退出循环
while语法解析
场景:只要条件满足,就一直循环下去 while [ 条件判断 ] do 执行语句 done
注意:条件支持的样式 命令、[[ 字符串表达式 ]]、(( 数字表达式 ));true是一个特殊的条件,代表条件永远成立
简单实践
实践1-输出指定的范围数字
while_num_list.sh
#!/bin/bash
# 功能:while的输出5范围以内的数字
# 定制初始变量值
a=1
# 定制内容输出逻辑
while [ "${a}" -le 5 ]
do
echo -n "${a} "
# 每输出一次数据,数据值+1
a=$((a+1))
done
echo
/bin/bash while_num_list.sh
1 2 3 4 5
while案例
案例实践
统计实践1- 计算1+2+...+100 的结果
cat while_odd_num.sh
#!/bin/bash
# 功能:while统计数据之和
# 定制普通变量
all_sum=0
odd_sum=0
# 定制所有数据求和逻辑
i=1
while ((i<=100))
do
let all_sum+=i
let i++
done
# 定制所有奇数求和逻辑
i=1
while ((i<=100))
do
let odd_sum+=i
let i+=2
done
# 信息输出
echo -e "\e[31m所有数据之和: ${all_sum}\e[0m"
echo -e "\e[31m所有奇数之和: ${odd_sum}\e[0m"
/bin/bash while_odd_num.sh
所有数据之和: 5050
所有奇数之和: 2500
实践2-持续检测网站存活
while_site_healthcheck.sh
#!/bin/bash
# 功能:定制站点的检测功能
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通变量
read -p "> 请输入待测试站点域名: " site_addr
# 持久检测站点状态
while true
do
wget --spider -T5 -q -t2 ${site_addr} && echo "${site_addr} 站点正常" || echo "${site_addr} 站点异常"
sleep 1
done
/bin/bash while_site_healthcheck.sh
> 请输入待测试站点域名: www.baidu.com
www.baidu.com 站点正常
www.baidu.com 站点正常
read实践
while中有一种特殊的语法,while read line 它可以从文本中逐行读取相关的内容,然后存储到一个临时变量line中,然后我们后续就可以逐行对文本内容进行操作
# 语法解读
# 样式1:cat提前读
cat a.log | while read line
do
echo "File: ${line}"
done
# 样式2:exec提前读
exec < a.log
while read line
do
echo "${line}"
done
# 样式3:结尾导入读
while read line
do
echo "File: ${line}"
done < a.log
# 注意:方法1和3可以直接在命令行来实验,但是方法2必须在脚本中才能实验
# 实践1-命令行实践
# 方法1实践读取文件
cat /etc/hosts | while read line;do echo "File: ${line}";done
File: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
File: ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# 方法3实践读取文件
while read line;do echo "File: ${line}";done < /etc/hosts
File: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
File: ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
实践2-脚本实践
while_read_file.sh
#!/bin/bash
# 功能:while的exec读取文件内容
# 定制普通变量
read -p "> 请输入待读取的文件路径: " file_path
# 持久检测站点状态
exec < ${file_path}
while read line
do
echo "File: ${line}"
done
/bin/bash while_read_file.sh
> 请输入待读取的文件路径: /etc/hosts
File: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
File: ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
嵌套案例
这里的嵌套实践,与选择语句的嵌套实践基本一致,只不过组合的方式发生了一些变化。常见的组合样式如下:
while嵌套while语句 while 循环条件 do while 循环条件语句 done
while嵌套if语句 while 循环条件 do if 条件控制语句 done
简单实践
while嵌套while语句实践1-输出99乘法表
while_nine_table.sh
#!/bin/bash
# 功能:while打印99乘法表
# 定制打印99乘法表的业务逻辑
# 对第一个位置的数字进行循环
num1=1
while [ ${num1} -le 9 ]
do
# 对第二个位置的数字进行循环
num2=1
while [ ${num2} -le ${num1} ]
do
# 信息输出,\t\c 的目的是删除后续信息,生成的内容是固定长度
echo -e "\e[$[RANDOM%9+31]m${num1}x${num2}=$[num1*num2]\e[0m\t\c"
num2=$[$num2+1]
done
echo # 一个子循环一行内容
num1=$[$num1+1]
done
/bin/bash while_nine_table.sh
1x1=1
2x1=2 2x2=4
3x1=3 3x2=6 3x3=9
4x1=4 4x2=8 4x3=12 4x4=16
5x1=5 5x2=10 5x3=15 5x4=20 5x5=25
6x1=6 6x2=12 6x3=18 6x4=24 6x5=30 6x6=36
7x1=7 7x2=14 7x3=21 7x4=28 7x5=35 7x6=42 7x7=49
8x1=8 8x2=16 8x3=24 8x4=32 8x5=40 8x6=48 8x7=56 8x8=64
9x1=9 9x2=18 9x3=27 9x4=36 9x5=45 9x6=54 9x7=63 9x8=72 9x9=81
while嵌套if语句实践2-手机发送短信1次/1毛,余额低于1毛提示无法发送请充值
while_mobile_bill.sh
#!/bin/bash
# 功能:while提示收集发短信
# 定制普通变量
read -p "> 请输入收集话费余额(元): " mobile_bill
# 定制普通变量
sms_num=0
bull_count=$[$mobile_bill * 10]
while [ $bull_count -ge 0 ]
do
sms_num=$(($sms_num+1))
if [ $bull_count -lt 1 ];then
echo "剩余费用不足,请充话费!"
else
echo "截至目前,您已发送 ${sms_num} 条短信。"
fi
bull_count=$(($bull_count-1))
sleep 0.1
done
/bin/bash while_mobile_bill.sh
> 请输入收集话费余额(元): 1
截至目前,您已发送 1 条短信。
截至目前,您已发送 2 条短信。
截至目前,您已发送 3 条短信。
截至目前,您已发送 4 条短信。
截至目前,您已发送 5 条短信。
截至目前,您已发送 6 条短信。
截至目前,您已发送 7 条短信。
截至目前,您已发送 8 条短信。
截至目前,您已发送 9 条短信。
截至目前,您已发送 10 条短信。
剩余费用不足,请充话费!
until循环
until基础
until命令本质上与while循环一致,区别在于until走循环之前会对输入的值进行条件判断,如果不满足条件的话,才会进入到循环体中执行对应的语句,否则的话就退出循环
until语法解析:只要条件不满足,就一直循环下去 until [ 条件判断 ] do 执行语句 done
注意:条件支持的样式 命令、[[ 字符串表达式 ]]、(( 数字表达式 ));true是一个特殊的条件,代表条件永远成立
简单实践
实践1-输出指定的范围数字
until_num_list.sh
#!/bin/bash
# 功能:until的输出5范围以内的数字
# 定制初始变量值
a=1
# 定制内容输出逻辑
until [ "${a}" -gt 5 ]
do
echo -n "${a} "
# 每输出一次数据,数据值+1
a=$((a+1))
done
echo
/bin/bash until_num_list.sh
1 2 3 4 5
实践2- 计算1+2+...+100 的结果
until_odd_num.sh
#!/bin/bash
# 功能:until统计数据之和
# 定制普通变量
all_sum=0
odd_sum=0
# 定制所有数据求和逻辑
i=1
until ((i>100))
do
let all_sum+=i
let i++
done
# 定制所有奇数求和逻辑
i=1
until ((i>100))
do
let odd_sum+=i
let i+=2
done
# 信息输出
echo -e "\e[31m所有数据之和: ${all_sum}\e[0m"
echo -e "\e[31m所有奇数之和: ${odd_sum}\e[0m"
/bin/bash until_odd_num.sh
所有数据之和: 5050
所有奇数之和: 2500
嵌套案例
这里的嵌套实践,与while语句的嵌套实践基本一致,只不过组合的方式发生了一些变化。常见的组合样式如下:
until嵌套until语句 until 循环条件 do until 循环条件语句 done
until嵌套if语句 until 循环条件 do if 条件控制语句 done
简单实践
until嵌套until语句实践1-输出99乘法表
until_nine_table.sh
#!/bin/bash
# 功能:until打印倒序99乘法表
# 定制打印99乘法表的业务逻辑
# 对第一个位置的数字进行循环
num1=9
until [ ${num1} -eq 0 ]
do
# 对第二个位置的数字进行循环
num2=1
until [ ${num2} -gt ${num1} ]
do
# 信息输出,\t\c 的目的是删除后续信息,生成的内容是固定长度
echo -e "\e[$[RANDOM%9+31]m${num1}x${num2}=$[num1*num2]\e[0m\t\c"
num2=$[$num2+1]
done
num1=$[$num1-1]
echo # 一个子循环一行内容
done
/bin/bash until_nine_table.sh
9x1=9 9x2=18 9x3=27 9x4=36 9x5=45 9x6=54 9x7=63 9x8=72 9x9=81
8x1=8 8x2=16 8x3=24 8x4=32 8x5=40 8x6=48 8x7=56 8x8=64
7x1=7 7x2=14 7x3=21 7x4=28 7x5=35 7x6=42 7x7=49
6x1=6 6x2=12 6x3=18 6x4=24 6x5=30 6x6=36
5x1=5 5x2=10 5x3=15 5x4=20 5x5=25
4x1=4 4x2=8 4x3=12 4x4=16
3x1=3 3x2=6 3x3=9
2x1=2 2x2=4
1x1=1
until嵌套if语句实践2-手机发送短信1次/1毛,余额低于1毛提示无法发送请充值
until_mobile_bill.sh
#!/bin/bash
# 功能:until提示收集发短信
# 定制普通变量
read -p "> 请输入收集话费余额(元): " mobile_bill
# 定制普通变量
sms_num=0
bull_count=$[$mobile_bill * 10]
until [ $bull_count -lt 0 ]
do
sms_num=$(($sms_num+1))
if [ $bull_count -lt 1 ];then
echo "剩余费用不足,请充话费!"
else
echo "截至目前,您已发送 ${sms_num} 条短信。"
fi
bull_count=$(($bull_count-1))
sleep 0.1
done
/bin/bash until_mobile_bill.sh
> 请输入收集话费余额(元): 1
截至目前,您已发送 1 条短信。
截至目前,您已发送 2 条短信。
截至目前,您已发送 3 条短信。
截至目前,您已发送 4 条短信。
截至目前,您已发送 5 条短信。
截至目前,您已发送 6 条短信。
截至目前,您已发送 7 条短信。
截至目前,您已发送 8 条短信。
截至目前,您已发送 9 条短信。
截至目前,您已发送 10 条短信。
剩余费用不足,请充话费!
循环控制
控制解析
所谓的流程控制,主要针对的是,当我们处于流程步骤执行的过程中,因为某些特殊的原因,不得不停止既定的操作进行步骤的调整,常见的临时调整场景如下:
- continue控制,满足条件的情况下,临时停止当前的循环,直接进入到下一循环
- break控制,满足条件的情况下,提前退出当前的循环
- exit控制,直接退出当前循环的程序
- shift控制,依次从循环列表中读取读取内容,并将读取的内容从列表中剔除
exit实践
exit在shell中是一个特殊的程序退出信号,不仅仅可以直接退出当前程序,还可以设定退出后的状态返回值,使用方式如下:exit num
注意:
- 在脚本中遇到exit命令,脚本立即终止;终止退出状态取决于exit命令后面的数字
- 如果exit后面无数字,终止退出状态取决于exit前面命令执行结果
实践1- 设定退出状态值
# 网段内主机地址的存活性探测
ping -c1 -W1 192.168.91.101 &> /dev/null && echo '192.168.91.101 is up' || (echo '192.168.91.101 is unreachable'; exit 1)
192.168.91.101 is up
ping -c1 -W1 192.168.91.102 &> /dev/null && echo '192.168.91.102 is up' || (echo '192.168.91.102 is unreachable'; exit 6)
192.168.91.102 is unreachable
echo $?
6
# 服务器网址探测
curl -s -o /dev/null baidu.com &> /dev/null && echo 'baidu.com is up' || (echo 'baidu.com is unreachable'; exit 7)
baidu.com is up
curl -s -o /dev/null baidu.com1 &> /dev/null && echo 'baidu.com1 is up' || (echo 'baidu.com1 is unreachable'; exit 7)
baidu.com1 is unreachable
echo $?
7
实践2-嵌套循环中exit退出程序
exit_multi_for.sh
#!/bin/bash
# 功能:exit退出脚本程序
# 外层循环遍历1-5
for var1 in {1..5}
do
# 内层循环遍历a-d
for var2 in {a..d}
do
# 判断退出条件,var1是2或者var2是c就退出内层循环
if [ $var1 -eq 2 -o "$var2" == "c" ]
then
exit 111
else
echo "$var1 $var2"
fi
done
done
/bin/bash exit_multi_for.sh
1 a
1 b
echo $?
111
break实践
break命令是在处理过程中终止循环的一种简单方法。可以使用break命令退出任何类型的循环,包括for、while、until等
break主要有两种场景的表现样式:
- 单循环场景下,break是终止循环,仅有一层 while 、for、until等
- 嵌套循环场景下,break是可以终止内层循环和外层循环,存在多层while、for、until嵌套等
break语法格式: for 循环列表 do ... break num done 注意: 单循环下,break就代表退出循环 多循环下,break的num大于嵌套的层数,就代表退出循环
简单实践
实践1-break终止单层循环
break_single_while.sh
#!/bin/bash
# 功能:break退出单层循环
while true
do
read -p "输入你的数字,最好在 1 ~ 5: " aNum
case $aNum in
1|2|3|4|5)
echo "你的数字是 $aNum!"
;;
*)
echo "你选择的数字没在 1 ~ 5, 退出!"
break
;;
esac
done
/bin/bash break_single_while.sh
输入你的数字,最好在 1 ~ 5: 1
你的数字是 1!
输入你的数字,最好在 1 ~ 5: 3
你的数字是 3!
输入你的数字,最好在 1 ~ 5: 8
你选择的数字没在 1 ~ 5, 退出!
实践2-多层循环下break退出内层循环
break_multi_in_while.sh
#!/bin/bash
# 功能:break退出内层循环
# 外层循环遍历1-3
for var1 in {1..3}
do
# 内层循环遍历a-d
for var2 in {a..d}
do
# 判断退出条件,var1是2或者var2是c就退出内层循环
if [ $var1 -eq 2 -o "$var2" == "c" ]
then
break
else
echo "$var1 $var2"
fi
done
done
/bin/bash break_multi_in_while.sh
1 a
1 b
3 a
3 b
实践3-多层循环下break退出外层循环
break_multi_out_while.sh
#!/bin/bash
# 功能:break退出外层循环
# 外层循环遍历1-3
for var1 in {1..3}
do
# 内层循环遍历a-d
for var2 in {a..d}
do
# 判断退出条件,var1是2或者var2是c就退出内层和外层循环
if [ $var1 -eq 2 -o "$var2" == "c" ]
then
break 2
else
echo "$var1 $var2"
fi
done
done
/bin/bash break_multi_out_while.sh
1 a
1 b
continue实践
continue命令是在处理过程中跳出循环的一种简单方法。可以使用continue命令跳出当前的循环直接进入到下一个循环,包括for、while、until等
continue主要有两种场景的表现样式:
- 单循环场景下,continue是跳出当前循环,仅有一层 while 、for、until等
- 嵌套循环场景下,continue是可以跳出内层循环和外层循环,存在多层while、for、until嵌套等
continue语法格式: for 循环列表 do ... continue num done 注意: 单循环下,continue就代表跳出当前循环 多循环下,continue的num就代表要继续的循环级别
简单实践
实践1-continue跳出当前单层循环
continue_single_while.sh
#!/bin/bash
# 功能:continue退出单层循环
while true
do
read -p "输入你的数字,最好在 1 ~ 5: " aNum
case $aNum in
1|2|3|4|5)
echo "你的数字是 $aNum!"
;;
*)
echo "你选择的数字没在 1 ~ 5, 退出!"
continue
;;
esac
done
/bin/bash continue_single_while.sh
输入你的数字,最好在 1 ~ 5: 2
你的数字是 2!
输入你的数字,最好在 1 ~ 5: 6
你选择的数字没在 1 ~ 5, 退出!
输入你的数字,最好在 1 ~ 5: 1
你的数字是 1!
输入你的数字,最好在 1 ~ 5:
# 即使输出的数据不是我们想要的,也不会退出循环逻辑
实践2-多层循环下continue跳出当前内层循环
continue_multi_in_while.sh
#!/bin/bash
# 功能:continue跳出当前内层循环
# 外层循环遍历1-3
for var1 in {1..3}
do
# 内层循环遍历a-d
for var2 in {a..d}
do
# 判断跳出条件,var1是2或者var2是c就跳出当前内层循环,继续执行下一循环
if [ $var1 -eq 2 -o "$var2" == "c" ]
then
continue
else
echo "$var1 $var2"
fi
done
done
/bin/bash continue_multi_in_while.sh
1 a
1 b
1 d
3 a
3 b
3 d
实践3-多层循环下continue跳出当前外层循环
continue_multi_out_while.sh
#!/bin/bash
# 功能:continue跳出当前外层循环
# 外层循环遍历1-3
for var1 in {1..3}
do
# 内层循环遍历a-d
for var2 in {a..d}
do
# 判断退出条件,var1是2或者var2是c就跳出当前外层循环,直接将外层循环跳到下一循环继续执行
if [ $var1 -eq 2 -o "$var2" == "c" ]
then
continue 2
else
echo "$var1 $var2"
fi
done
done
/bin/bash continue_multi_out_while.sh
1 a
1 b
3 a
3 b
shift实践
shift是一个特殊的循环控制命令,它的特点主要是依次从输入信息列表中获取相关的参数数据值,然后走循环。
shift语法格式: for 循环列表 do ... shift done 注意:shift 从用户输入的信息中提取第一个位置的数据内容,每走一个循环,从下一个位置获取输入参数
简单实践
实践1-依次获取所有参数的内容
shift_get_args.sh
#!/bin/bash
#功能:shift依次从输入的参数列表中获取内容
# 定制一套循环逻辑,直到参数位置内容为空
# 每次从脚本的第一个位置获取的内容在逐渐变化中
until [ -z "$1" ]
do
echo "脚本的第1个位置参数内容是: $1, 当前脚本参数数量: $#"
# 接收完第一个参数后,直接将起始值移至下一个位置
shift
done
/bin/bash shift_get_args.sh aa bb cc dd ee
脚本的第1个位置参数内容是: aa, 当前脚本参数数量: 5
脚本的第1个位置参数内容是: bb, 当前脚本参数数量: 4
脚本的第1个位置参数内容是: cc, 当前脚本参数数量: 3
脚本的第1个位置参数内容是: dd, 当前脚本参数数量: 2
脚本的第1个位置参数内容是: ee, 当前脚本参数数量: 1
实践2-创建指定的系统用户
shift_add_user.sh
#!/bin/bash
# 功能:shift批量创建指定的用户
# 提示:
# /dev/random和/dev/urandom设备文件会生成随机数,第一个依赖系统中断
# 定制普通变量
user_file='/tmp/user.txt'
# 保证文件可用
[ -f ${user_file} ] && > ${user_file}
# 定制批量创建用户的业务逻辑
if [ $# -ne 0 ]
then
# 注意: 这里用的是 -n,后面的$1两侧要有"",如果用until语句的话使用 -z表达式
while [ -n "$1" ]
do
# 创建用户
useradd $1
# 生成密码
password=$(head /dev/urandom | tr -dc '[:alnum:]' | head -c 8)
# 为用户添加密码
echo ${password} | passwd --stdin $1 > /dev/null 2>&1
# 信息输出
echo "用户: $1, 密码: ${password}" >> ${user_file}
echo -e "\e[31m用户 $1 创建成功\e[0m"
# 移动输入参数
shift
done
else
echo -e "\e[31mUsage: /bin/bash $0 arg_list\e[0m"
fi
/bin/bash shift_add_user.sh
Usage: /bin/bash shift_add_user.sh arg_list
/bin/bash shift_add_user.sh zhangsan lisi wangwu zhaoliu
用户 zhangsan 创建成功
用户 lisi 创建成功
用户 wangwu 创建成功
用户 zhaoliu 创建成功
# 清理环境
for user in zhangsan lisi wangwu zhaoliu; do userdel -r $user; done
select条件控制
基础实践
select和其他循环控制不一样,它一般用来增强交互性,它可以显示出带编号的菜单,用户输入不同的编号就可以选择不同的菜单,并执行不同的功能。虽然select本身就是循环,但是select的循环仅限于接收列表中的输入信息,所以在生产中,我们一般将Select 搭配 case来使用,从而实现更为复杂的菜单控制选项
select 表达式是一种bash的扩展应用,动作包括:
- 自动用1,2,3,4列出菜单(没有echo指令,自动显示菜单)
- 自动read输入选择(没有 read指令,自动输入)
- 赋值给变量(没有赋值指令,自动输入数字后,赋值字符串给变量)
语法解读
select 变量名 in input_list do 循环语句 done
语法解读: 1 select 从 input_list 获取相关的值,然后输出到一个菜单列表,元素格式如下: 序号) 菜单条目 2 用户输入信息的标识使用PS3 的值,默认值是#?。 我们可以定制PS3环境变量 3 如果用户输入菜单序号,则输出菜单内容 4 select的退出方式有很多种 强制退出:Ctrl+D 组合键 普通退出:结合break方式
简单实践
实践1-语法实践
simple_select.sh
#!/bin/bash
# select语句的使用
# 定制select循环逻辑
echo "您喜欢的操作系统是?"
select osname in "HarmonyOS" "Linux" "Windows" "Ubuntu"
do
echo "您选择的操作系统是: $osname."
done
# 使用Ctrl + D 方式退出循环
/bin/bash simple_select.sh
您喜欢的操作系统是?
1) HarmonyOS
2) Linux
3) Windows
4) Ubuntu
#? 1
您选择的操作系统是: HarmonyOS.
#? 5
您选择的操作系统是: .
#?
实践2-定制信息+优雅退出
simple_select.sh
#!/bin/bash
# select语句的使用
# 定制普通环境变量
PS3='请选择菜单序号: '
# 定制select循环逻辑
echo "您喜欢的操作系统是?"
select osname in "HarmonyOS" "Linux" "Windows" "Ubuntu"
do
echo "您选择的操作系统是: $osname."
break
done
/bin/bash simple_select.sh
您喜欢的操作系统是?
1) HarmonyOS
2) Linux
3) Windows
4) Ubuntu
请选择菜单序号: 1
您选择的操作系统是: HarmonyOS.
案例实践
case实践
实践3-整合case实践
simple_select_case.sh
#!/bin/bash
# select语句的使用
# 定制普通环境变量
PS3='请选择菜单序号: '
# 定制select循环逻辑
echo "您喜欢的操作系统是?"
select osname in "HarmonyOS" "Linux" "Windows" "Ubuntu"
do
echo "-----------------------------"
case $osname in
"HarmonyOS")
echo -e "\e[31m$osname 是华为推出的操作系统。\e[0m"
break;;
"Linux")
echo -e "\e[31m$osname 是比较常用的操作系统。\e[0m"
break;;
"Windows")
echo -e "\e[31m$osname 是一个桌面的操作系统。\e[0m"
break;;
"Ubuntu")
echo -e "\e[31m$osname 是一个很好的操作系统。\e[0m"
break;;
*)
echo -e "\e[31m您输入的是无效信息,再来一次!!!\e[0m"
break;;
esac
done
/bin/bash simple_select_case.sh
您喜欢的操作系统是?
1) HarmonyOS
2) Linux
3) Windows
4) Ubuntu
请选择菜单序号: 1
-----------------------------
HarmonyOS 是华为推出的操作系统。
/bin/bash simple_select_case.sh
您喜欢的操作系统是?
1) HarmonyOS
2) Linux
3) Windows
4) Ubuntu
请选择菜单序号: 5
-----------------------------
您输入的是无效信息,再来一次!!!
软件部署
select定制安装软件的界面功能
soft_install_select.sh
#!/bin/bash
# 功能:定制软件应用的部署功能
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通环境变量
PS3='请选择软件序号: '
# 定制数组变量
soft_name=(Nginx Apache Tomcat)
# 定制select循环逻辑
echo "您需要安装的Web软件是?"
select soft in ${soft_name[@]}
do
echo "-----------------------------"
case $soft in
"Nginx")
echo -e "\e[31m开始安装 $soft 软件环境...\e[0m"
break;;
"Apache")
echo -e "\e[31m开始安装 $soft 软件环境...\e[0m"
break;;
"Tomcat")
echo -e "\e[31m开始安装 $soft 软件环境...\e[0m"
break;;
*)
echo -e "\e[31m您输入的是无效信息,再来一次!!!\e[0m"
break;;
esac
done
/bin/bash soft_install_select.sh
您需要安装的Web软件是?
1) Nginx
2) Apache
3) Tomcat
请选择软件序号: 1
-----------------------------
开始安装 Nginx 软件环境...
/bin/bash soft_install_select.sh
您需要安装的Web软件是?
1) Nginx
2) Apache
3) Tomcat
请选择软件序号: 4
-----------------------------
您输入的是无效信息,再来一次!!!