13.Shell脚本修炼手册---玩转 CASE 语句(应用场景与实践技巧)
case 条件语句的应用实践
文章目录
- case 条件语句的应用实践
- case 条件语句的核心逻辑与语法
- 核心逻辑
- 语法格式
- 生活化举例
- 基础实践:判断用户输入的数字(1-3)
- 实验流程
- 脚本实现
- 执行验证
- 实践:给输出字符串添加颜色
- 实验流程
- 脚本实现
- 执行验证
- 企业级案例:用 case 管理系统服务
- 案例 1:控制 sshd 服务(启停 / 重启 / 状态查询)
- 实验流程
- 脚本实现(简化版)
- 执行验证
- 案例 2:用户管理脚本(添加 / 删除 / 查询用户)
- 需求说明
- 实验流程
- 脚本实现
- 执行验证
- 综合实验:批量配置 IP 地址和主机名
- 需求说明
- 实验流程
- 脚本实现
- 执行验证
- 本章小结
在 Shell 编程中,我们经常需要根据不同的条件执行不同的操作。比如判断用户输入的指令是 “start” 还是 “stop”,再执行对应的服务启停操作。这时除了用if/elif/else
多分支语句,case
条件语句会是更合适的选择 —— 它的结构更工整,逻辑更清晰,尤其适合处理固定值的多分支判断场景,在系统服务脚本等企业级场景中非常常用。
case 条件语句的核心逻辑与语法
核心逻辑
case
语句的工作方式就像 “对号入座”:
程序会拿一个变量的值,逐个与预设的 “标签”(值 1、值 2、值 3…)比对。
- 一旦找到匹配的标签,就执行该标签后的指令,直到遇到
;;
(相当于 “结束当前分支”),然后跳出整个case
语句; - 如果所有标签都不匹配,就执行
*)
后的指令(相当于 “其他情况”),这部分可以理解为if
语句中的else
; - 标签部分还支持用
|
表示 “或”(比如start|begin
表示匹配 “start” 或 “begin”)。
语法格式
case "变量值" in标签1) # 当变量值等于"标签1"时指令1 # 执行的操作(可以是多行命令);; # 结束当前分支标签2|标签3) # 变量值等于"标签2"或"标签3"时指令2;;*) # 所有标签都不匹配时指令3 # 默认操作(通常是提示信息);; # 此处的;;可以省略,但建议保留以保持格式统一
esac # case的反写,表示语句结束
生活化举例
用一个更形象的例子理解case
逻辑:
case "用户的考试分数" in90-100) # 分数在90到100之间echo "优秀";;60-89) # 分数在60到89之间echo "及格";;0-59) # 分数在0到59之间echo "不及格";;*) # 其他情况(比如输入非数字)echo "请输入有效的分数";;
esac
基础实践:判断用户输入的数字(1-3)
实验流程
- 创建脚本文件,通过
read
命令获取用户输入的数字; - 用
case
语句判断数字是否为 1、2、3,分别输出对应信息; - 若输入其他数字,提示 “请输入 1-3 之间的数字”;
- 执行脚本,验证不同输入的结果。
脚本实现
#!/bin/bash
# 提示用户输入1-3之间的数字,并将输入保存到变量num中
read -p "请输入一个1-3之间数字:" num# 用case判断num的值
case $num in1) # 若num等于1echo "您输入的数字是:$num" # 输出提示;; # 结束当前分支2) # 若num等于2echo "您输入的数字是:$num";;3) # 若num等于3echo "您输入的数字是:$num";;*) # 若num是其他值echo "请输入一个1-3之间数字。" # 提示输入错误;;
esac
执行验证
# 输入1(符合条件)
[bq@shell ~]$ bash case1.sh
请输入一个1-3之间数字:1
您输入的数字是:1# 输入4(不符合条件)
[bq@shell ~]$ bash case1.sh
请输入一个1-3之间数字:4
请输入一个1-3之间数字。# 输入字母(不符合条件)
[bq@shell ~]$ bash case1.sh
请输入一个1-3之间数字:a
请输入一个1-3之间数字。
实践:给输出字符串添加颜色
实验流程
- 创建脚本,接收参数(PASS/FAIL/DONE);
- 用
case
语句根据参数,通过 ANSI 转义字符输出对应颜色的文字(绿色 PASS、红色 FAIL、紫色 DONE); - 若参数错误,提示正确用法;
- 执行脚本,验证颜色输出效果。
脚本实现
#!/bin/bash
# 根据传入的参数($1)输出带颜色的文字
case $1 inPASS)# \033[1;32m:设置文字为高亮绿色;\033[0;39m:恢复默认颜色echo -e '\033[1;32mPASS\033[0;39m';;FAIL)# \033[1;31m:设置文字为高亮红色echo -e '\033[1;31mFAIL\033[0;39m';;DONE)# \033[1;35m:设置文字为高亮紫色echo -e '\033[1;35mDONE\033[0;39m';;*)# 若参数不是PASS/FAIL/DONE,提示用法echo "Usage: $0 PASS|FAIL|DONE";;
esac
执行验证
# 输出绿色PASS
[bq@shell ~]$ bash case2.sh PASS
PASS # 实际显示为绿色高亮# 输出红色FAIL
[bq@shell ~]$ bash case2.sh FAIL
FAIL # 实际显示为红色高亮# 输出紫色DONE
[bq@shell ~]$ bash case2.sh DONE
DONE # 实际显示为紫色高亮# 参数错误时提示用法
[bq@shell ~]$ bash case2.sh ABC
Usage: case2.sh PASS|FAIL|DONE
企业级案例:用 case 管理系统服务
案例 1:控制 sshd 服务(启停 / 重启 / 状态查询)
实验流程
- 创建脚本,接收参数(start/stop/restart/reload/status);
- 用
case
语句根据参数调用systemctl
命令操作 sshd 服务; - 若参数错误,提示正确用法;
- 执行脚本,验证服务操作是否生效。
脚本实现(简化版)
#!/bin/bash
# 根据传入的参数($1)管理sshd服务
case $1 in # 匹配start/stop/restart/reload/status中的任意一个参数start | stop | restart | reload | status)# 直接用参数作为systemctl的操作指令(例如$1为start时,执行systemctl start sshd)systemctl $1 sshd;;*)# 参数错误时,提示用法echo "Usage: case-ssh start|stop|restart|reload|status";;
esac
执行验证
# 启动sshd服务
[root@shell ~]# bash case-ssh start
# 查看服务状态(应显示"active (running)")
[root@shell ~]# bash case-ssh status
● sshd.service - OpenSSH server daemonLoaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)Active: active (running) since ...# 停止sshd服务
[root@shell ~]# bash case-ssh stop
# 再次查看状态(应显示"inactive (dead)")
[root@shell ~]# bash case-ssh status
● sshd.service - OpenSSH server daemonLoaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)Active: inactive (dead) since ...
案例 2:用户管理脚本(添加 / 删除 / 查询用户)
需求说明
通过脚本user-mgr
管理/etc/users
文件中的用户清单,支持:
- 添加用户(参数
-add|-a
):若用户已存在则提示,否则写入文件; - 删除用户(参数
-del|-d
):若用户不存在则提示,否则从文件中删除; - 查询用户(参数
-search|-s
):判断用户是否在文件中; - 限制仅 root 用户执行,且必须传入 2 个参数(操作 + 用户名)。
实验流程
- 创建脚本,先判断执行权限(是否为 root)和参数数量(是否为 2 个);
- 定义函数实现用户查询、添加、删除逻辑(用
grep
检查存在性,sed
删除行,chattr
锁定文件防止误改); - 用
case
语句根据参数调用对应函数; - 执行脚本,验证添加、查询、删除功能。
脚本实现
#!/bin/bash# 检查是否为root用户(非root则提示并退出)
[ $UID -ne 0 ] && echo '请使用root用户执行' && exit 1# 定义用户清单文件路径
users_info_file=/etc/users
# 若文件不存在则创建
[ -f ${users_info_file} ] || touch ${users_info_file}# 定义用法提示函数
function usage () {echo "Usage: user-mgr [ [-add|-a ] | [ -d|-del ] | [ -s|-search ] ] username" exit 2
}# 定义用户查询函数(参数:用户名)
function search_user () {# 用grep静默查询文件中是否存在"username: 用户名"(-q:不输出结果,仅返回状态)if grep -q "username: $1" ${users_info_file};thenecho "$1 已存在"return 0 # 存在则返回0(成功状态)elseecho "$1 不存在"return 1 # 不存在则返回1(失败状态)fi
}# 定义用户添加函数(参数:用户名)
function add_user () {# 先调用查询函数,若用户已存在($?为0)则提示search_user $1 &>/dev/null # &>/dev/null:忽略查询函数的输出if [ $? -eq 0 ];thenecho "$1 已存在,无法重复添加"elsechattr -i ${users_info_file} # 解除文件锁定(-i:移除不可修改属性)# 向文件追加用户信息,并提示成功echo "username: $1" >> ${users_info_file} && echo "添加用户 $1 成功"chattr +i ${users_info_file} # 重新锁定文件(+i:设置不可修改属性)fi
}# 定义用户删除函数(参数:用户名)
function del_user () {# 先调用查询函数,若用户不存在($?为1)则提示search_user $1 &>/dev/nullif [ $? -eq 1 ];thenecho "$1 不存在,无法删除"elsechattr -i ${users_info_file} # 解除文件锁定# 用sed删除包含"username: 用户名"的行,并提示成功sed -i "/username: $1/d" ${users_info_file} && echo "删除用户 $1 成功"chattr +i ${users_info_file} # 重新锁定文件fi
}# 检查参数数量(若不是2个则调用用法提示)
[ $# -ne 2 ] && usage# 根据第一个参数(操作类型)调用对应函数
case $1 in-a|-add)shift # 移除第一个参数(保留用户名作为后续函数的参数)add_user $1;;-d|-del)shiftdel_user $1;;-s|-search)shiftsearch_user $1;;*)usage # 参数不匹配时提示用法;;
esac
执行验证
# 初始状态:/etc/users为空
[root@shell ~]# cat /etc/users# 查询不存在的用户(bq)
[root@shell ~]# bash user-mgr -s bq
bq 不存在# 添加用户bq
[root@shell ~]# bash user-mgr -a bq
添加用户 bq 成功# 再次查询bq(已存在)
[root@shell ~]# bash user-mgr -s bq
bq 已存在# 查看用户文件(已写入)
[root@shell ~]# cat /etc/users
username: bq# 重复添加bq(提示已存在)
[root@shell ~]# bash user-mgr -a bq
bq 已存在,无法重复添加# 删除用户bq
[root@shell ~]# bash user-mgr -d bq
删除用户 bq 成功# 再次删除bq(提示不存在)
[root@shell ~]# bash user-mgr -d bq
bq 不存在,无法删除# 非root用户执行(提示权限不足)
[bq@shell ~]$ bash user-mgr -s bq
请使用root用户执行
综合实验:批量配置 IP 地址和主机名
需求说明
根据输入的数字(21-34),自动配置服务器的 IP 地址和主机名,对应关系如下:
输入参数 | IP 地址 | 主机名 |
---|---|---|
21 | 10.1.8.21 | ha1.market.com |
22 | 10.1.8.22 | ha2.market.com |
23 | 10.1.8.23 | proxy1.market.com |
24 | 10.1.8.24 | proxy2.market.com |
25 | 10.1.8.25 | company1.market.com |
26 | 10.1.8.26 | company2.market.com |
27 | 10.1.8.27 | shop1.market.com |
28 | 10.1.8.28 | shop2.market.com |
29 | 10.1.8.29 | db1.market.com |
30 | 10.1.8.30 | db2.market.com |
31 | 10.1.8.31 | storage.market.com |
32 | 10.1.8.32 | backup.market.com |
33 | 10.1.8.33 | network.market.com |
34 | 10.1.8.34 | client.market.com |
-
实验流程
- 创建脚本,接收一个参数(21-34);
- 定义函数
set_ip
:根据参数设置 IP 地址(通过nmcli
命令); - 定义函数
set_hostname
:用case
语句根据参数匹配对应的主机名,通过hostnamectl
设置; - 主函数调用上述两个函数,执行配置;
- 执行脚本后,用
ip addr
和hostname
命令验证结果。
脚本实现
#!/bin/bash# 定义网络接口名称(根据实际环境修改,如ens33、eth0等) interface=ens33# 定义域名后缀 domain=market.com# 用法提示函数(若参数不符合要求则调用) usage (){echo "Usage: $0 21-34" # 提示参数必须是21到34之间的数字exit 1 }# 设置IP地址的函数(参数:数字21-34) function set_ip () {if [ $# -eq 0 ]; then # 若未传参数,提示用法usage else# 用nmcli修改接口的IP地址(格式:10.1.8.参数/24)nmcli connection modify $interface ipv4.address 10.1.8.$1/24# 重新启用接口使配置生效nmcli connection up $interfaceecho "IP地址已设置为 10.1.8.$1/24"fi }# 设置主机名的函数(参数:数字21-34) function set_hostname () {# 根据参数匹配对应的主机名case $1 in21|22) # 21或22:主机名前缀为ha,序号=参数-20HOSTNAME=ha$(( $1 - 20 )).$domain;;23|24) # 23或24:前缀为proxy,序号=参数-22HOSTNAME=proxy$(( $1 - 22 )).$domain;;25|26) # 25或26:前缀为company,序号=参数-24HOSTNAME=company$(( $1 - 24 )).$domain;;27|28) # 27或28:前缀为shop,序号=参数-26HOSTNAME=shop$(( $1 - 26 )).$domain;;29|30) # 29或30:前缀为db,序号=参数-28HOSTNAME=db$(( $1 - 28 )).$domain;;31) # 31:固定主机名storageHOSTNAME=storage.$domain;;32) # 32:固定主机名backupHOSTNAME=backup.$domain;;33) # 33:固定主机名networkHOSTNAME=network.$domain;;34) # 34:固定主机名clientHOSTNAME=client.$domain;;*) # 其他参数:提示用法usage;;esac# 设置主机名hostnamectl set-hostname $HOSTNAMEecho "主机名已设置为 $HOSTNAME" }# 主函数:调用IP和主机名设置函数 function main() {set_ip $1set_hostname $1 }# 执行主函数(传入脚本接收的参数) main $*
执行验证
# 以参数21为例执行脚本 [root@shell ~]# bash set_network.sh 21 IP地址已设置为 10.1.8.21/24 主机名已设置为 ha1.market.com# 验证IP地址(ens33接口应显示10.1.8.21) [root@shell ~]# ip addr show ens33 ... inet 10.1.8.21/24 brd 10.1.8.255 scope global noprefixroute ens33 ...# 验证主机名(应显示ha1.market.com) [root@shell ~]# hostname ha1.market.com# 测试参数34(验证IP和主机名) [root@shell ~]# bash set_network.sh 34 IP地址已设置为 10.1.8.34/24 主机名已设置为 client.market.com[root@shell ~]# ip addr show ens33 | grep inet inet 10.1.8.34/24 brd 10.1.8.255 scope global noprefixroute ens33[root@shell ~]# hostname client.market.com
本章小结
- case 语句的适用场景:
当需要判断的变量值是固定的字符串或数字(如 start/stop、1-34 等)时,用case
比if/elif/else
更简洁易读,尤其适合编写服务管理脚本(如启停脚本)。 - 与 if 语句的区别:
case
:适合处理固定值的多分支判断,结构工整,逻辑清晰;if
:适合处理条件表达式判断(如数值大小、文件属性等),应用范围更广(几乎所有case
都能用if
实现,但反之不成立)。
- 核心语法要点:
- 用
in
引出标签列表,标签间用|
表示 “或”; - 每个分支用
;;
结束,默认分支用*)
表示; - 语句以
esac
(case 反写)结束。
- 用
如涉及版权问题请联系作者处理!!