目录
一、条件测试操作
Shell 脚本的 “智能” 源于对系统状态的精准判断,而条件测试正是实现这一能力的基础。通过test
命令或简化的[条件表达式]
形式,脚本能够对文件属性、数值关系、字符串内容及多重条件组合进行检测,为流程控制提供决策依据。
1. 文件测试:系统资源的 “身份验证”
文件测试是 Shell 脚本中最基础的检测手段,用于判断文件或目录的类型、存在性及权限属性。其核心选项遵循简洁的英文缩写规则,配合路径参数即可完成检测。
核心选项与应用场景
选项 | 功能描述 | 语法示例 | 返回值说明 |
---|---|---|---|
-d | 检测是否为目录 | [ -d /var/log ] | 0(是目录)/1(否) |
-e | 检测文件 / 目录是否存在 | [ -e /etc/hosts ] | 0(存在)/1(不存在) |
-f | 检测是否为普通文件 | [ -f /usr/bin/bash ] | 0(是文件)/1(否) |
-r | 检测当前用户是否可读 | [ -r ~/secret.txt ] | 0(可读)/1(不可读) |
-w | 检测当前用户是否可写 | [ -w ~/test.txt ] | 0(可写)/1(不可写) |
-x | 检测是否可执行 | [ -x /usr/sbin/nginx ] | 0(可执行)/1(不可执行) |
实战案例:智能创建数据备份目录
#!/bin/bash
# 脚本:create_backup_dir.sh
backup_dir="/data/backup"
# 检测目录是否存在,不存在则递归创建并设置权限
if [ ! -d "$backup_dir" ]; then
mkdir -p "$backup_dir"
chmod 755 "$backup_dir"
echo "备份目录已创建:$backup_dir"
fi
- 逻辑解析:
! -d
表示 “非目录”,结合mkdir -p
确保目录存在,chmod
设置安全权限,保障脚本健壮性。
2. 整数值比较:数字世界的逻辑推演
整数值比较用于判断两个整数的大小关系,常见于资源监控、阈值告警等场景。需注意运算符的英文缩写含义及与字符串比较的区别。
比较运算符详解
运算符 | 含义 | 语法示例 | 成立条件 |
---|---|---|---|
-eq | 等于(Equal) | [ $a -eq $b ] | \(a与\)b 数值相等 |
-ne | 不等于(Not Equal) | [ $a -ne $b ] | \(a与\)b 数值不等 |
-gt | 大于(Greater Than) | [ $a -gt $b ] | \(a大于\)b |
-lt | 小于(Lesser Than) | [ $a -lt $b ] | \(a小于\)b |
-le | 小于等于 | [ $a -le $b ] | \(a≤\)b |
-ge | 大于等于 | [ $a -ge $b ] | \(a≥\)b |
案例:服务器负载监控脚本
#!/bin/bash
# 脚本:load_monitor.sh
current_load=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | tr -d ' ')
# 判断1分钟负载是否超过CPU核心数(假设4核)
if [ $(echo "$current_load > 4" | bc) -eq 1 ]; then
echo "警告:1分钟负载达$current_load,超过4核阈值!"
# 此处可添加邮件告警或自动扩容逻辑
fi
- 数据处理:通过
uptime
获取负载信息,利用bc
命令实现浮点数值比较,突破传统整数比较限制。
3. 字符串比较:文本处理的精准匹配
字符串比较用于检测文本内容是否符合预期,需注意引号的使用(避免空值导致语法错误)和运算符的正确选择。
核心运算符与使用规范
运算符 | 功能 | 语法示例 | 成立条件 |
---|---|---|---|
= | 等于 | [ "$str1" = "$str2" ] | 字符串内容完全相同(区分大小写) |
!= | 不等于 | [ "$str1" != "$str2" ] | 字符串内容不同 |
-z | 为空 | [ -z "$str" ] | 字符串长度为 0(空值或未定义) |
-n | 非空 | [ -n "$str" ] | 字符串长度大于 0 |
案例:用户输入合法性校验
#!/bin/bash
# 脚本:input_validate.sh
read -p "请输入管理员密码:" -s password
if [ -z "$password" ]; then
echo -e "\n错误:密码不可为空!"
exit 1
elif [ ${#password} -lt 8 ]; then
echo -e "\n错误:密码长度需至少8位!"
exit 1
else
echo -e "\n密码校验通过,正在登录..."
# 此处可添加密码哈希校验逻辑
fi
- 多层校验:先判断是否为空,再检查长度,结合
-s
选项隐藏输入内容,提升安全性。
4. 逻辑测试:复杂条件的组合艺术
逻辑测试通过 “与”“或”“非” 操作组合多个条件,实现复杂场景的判断。需区分命令行符号(&&
/||
)与test
专用选项(-a
/-o
/!
)的使用场景。
逻辑运算符对比与示例:
类型 | 符号 | 功能 | 语法示例(等价写法) | 成立条件 |
逻辑与 | && | 前后条件均成立 | [ $a -gt 10 ] && [ $b -lt 20 ] [ $a -gt 10 -a $b -lt 20 ] | 两者同时为真 |
逻辑或 | || | 至少一个成立 | [ -e /data/file.txt ] || [ -w /data ] | 至少一个为真 |
逻辑非 | ! | 条件取反 | ![ -f file.txt ] | 条件不成立 |
案例:多条件组合的环境检测
#!/bash
# 脚本:env_check.sh
# 检测当前用户是否为root且系统版本为CentOS 7
if [ "$(whoami)" = "root" ] && [ "$(lsb_release -rs)" = "7.9" ]; then
echo "环境符合要求,开始部署..."
else
echo "错误:需以root用户在CentOS 7.9环境执行!"
exit 1
fi
- 条件组合:通过
whoami
和lsb_release
获取动态信息,&&
确保两个条件同时满足,保障脚本执行环境的正确性。
二、if 条件语句:流程控制的核心引擎
当脚本需要根据条件执行多行命令或复杂逻辑时,if
语句凭借清晰的分支结构成为首选。根据业务逻辑的复杂度,可分为单分支、双分支和多分支三种形式。
1. 单分支 if:条件成立时的专属执行路径
语法结构:
if 条件测试
then
命令序列 # 条件成立时执行
fi
执行逻辑:
- 执行条件测试,获取返回值
$?
; - 若返回 0(条件成立),执行
then
后的命令序列; - 否则跳过
then
块,继续执行后续代码。
实战:静默清理临时文件(仅当文件存在时)
#!/bash
# 脚本:clean_temp_files.sh
temp_file="/tmp/temp.log"
if [ -f "$temp_file" ]; then
rm -f "$temp_file"
echo "临时文件已清理:$temp_file"
fi
- 安全机制:通过
-f
确保仅删除存在的文件,避免rm
命令因文件不存在报错,提升脚本容错性。
2. 双分支 if:两种场景的明确分野
语法结构:
if 条件测试
then
命令序列1 # 条件成立时执行
else
命令序列2 # 条件不成立时执行
fi
典型应用:网络连通性检测脚本
#!/bash
# 脚本:network_check.sh
target="www.baidu.com"
ping -c 3 -W 5 "$target" &>/dev/null
if [ $? -eq 0 ]; then
echo "网络连通正常,目标地址:$target"
else
echo "网络连接失败,尝试重启网络服务..."
systemctl restart network
fi
- 输出控制:
&>/dev/null
屏蔽ping
命令的冗余输出,systemctl
实现故障自愈,增强脚本实用性。
3. 多分支 if:多层筛选的逻辑漏斗
语法结构:
if 条件测试1
then
命令序列1
elif 条件测试2
then
命令序列2
...
else
命令序列n # 所有条件不成立时执行
fi
案例:学生成绩分级系统(支持百分制与等级制)
#!/bash
# 脚本:score_grading.sh
read -p "请输入成绩(0-100):" score
if [ $score -ge 90 ]; then
grade="A(优秀)"
elif [ $score -ge 80 ]; then
grade="B(良好)"
elif [ $score -ge 70 ]; then
grade="C(中等)"
elif [ $score -ge 60 ]; then
grade="D(及格)"
else
grade="F(不及格)"
fi
echo "成绩$score对应等级:$grade"
- 逻辑顺序:从最高条件开始判断(如
-ge 90
),避免低优先级条件被覆盖,确保分级逻辑的正确性。
4. if 语句最佳实践
- 引号规范:变量引用必须加双引号(如
"$var"
),防止空值导致语法错误(如[ $var -eq 10 ]
在$var
为空时引发解析错误); - 错误处理:在
else
分支添加明确的错误提示和退出码(如exit 1
),便于上层脚本检测异常; - 嵌套优化:避免超过 3 层嵌套,复杂逻辑可拆分为独立函数或使用
case
语句,保持代码可读性。
三、case 分支语句:多选项匹配的高效解决方案
当变量存在固定取值(如命令参数、用户菜单选项)时,case
语句通过模式匹配实现更简洁的多分支控制,避免多层if-elif
的繁琐判断,提升代码的可维护性。
1. 语法结构与执行流程
语法格式:
case 变量值 in
模式1)
命令序列1
;;
模式2)
命令序列2
;;
*) # 默认模式(通配所有未匹配值)
命令序列n
;;
esac
执行逻辑:
- 将 “变量值” 依次与 “模式 1”“模式 2”… 进行匹配;
- 匹配成功则执行对应命令序列,遇到
;;
跳转至esac
结束; - 所有模式不匹配时执行默认模式
*
。
模式匹配规则
- 精确匹配:直接使用字符串(如
start)
匹配变量值为start
); - 范围匹配:
[a-z]
匹配小写字母,[0-9]
匹配单个数字; - 或操作:
start|stop)
匹配start
或stop
; - 通配符:
*
匹配任意字符(包括空值),?
匹配单个字符。
2. 实战案例:字符类型检测工具
#!/bash
# 脚本:char_type_detector.sh
read -p "请输入一个字符:" -n 1 char # 读取单个字符
echo -e "\n您输入的字符是:$char"
case "$char" in
[a-zA-Z]) # 匹配大小写字母
echo "类型:字母(ASCII码:$(printf "%d" "'$char'))"
;;
[0-9]) # 匹配数字
echo "类型:数字(数值:$char)"
;;
[[:space:]]) # 匹配空格、制表符等空白字符
echo "类型:空白字符"
;;
*) # 特殊字符(如!@#$%等)
echo "类型:特殊字符(ASCII码:$(printf "%d" "'$char'))"
;;
esac
- 高级特性:使用
-n 1
读取单个字符,printf "%d" "'$char'"
获取 ASCII 码,结合[[:space:]]
匹配所有空白字符,提升检测精度。
3. 生产级实践:系统服务控制脚本
#!/bash
# 脚本:service_manager.sh
service_name="httpd"
case "$1" in
start)
echo -n "启动$service_name服务..."
systemctl start "$service_name"
if [ $? -eq 0 ]; then
echo "成功"
else
echo "失败"
exit 1
fi
;;
stop)
echo -n "停止$service_name服务..."
systemctl stop "$service_name"
echo "成功"
;;
restart)
"$0" stop # 调用当前脚本的stop功能
"$0" start # 调用当前脚本的start功能
;;
status)
status=$(systemctl is-active "$service_name")
echo "$service_name服务状态:$status"
;;
*) # 处理未知参数
echo "用法:$0 {start|stop|restart|status}"
exit 1
;;
esac
- 脚本自调用:通过
"$0"
引用脚本自身路径,restart
分支实现 “先停止后启动” 的原子操作; - 状态查询:
systemctl is-active
直接返回服务状态(active/inactive),比通过$?
判断更直观。
4. case vs if:场景选择指南
场景特征 | case 优势 | if 优势 |
---|---|---|
固定取值判断(如命令参数) | 模式匹配简洁,结构清晰 | 动态条件组合(如文件权限 + 数值范围) |
多选项菜单驱动 | 选项对应明确,易于扩展 | 非固定值复杂逻辑(如字符串包含判断) |
输入类型分类(如字母 / 数字) | 范围匹配高效 | 跨类型条件组合(如文件存在且用户可读) |
四、常见错误与避坑指南
1. 空格引发的语法错误
- 错误示例:
[ $a -eq $b ]
(缺少空格导致解析错误) - 正确写法:
[ "$a" -eq "$b" ]
(确保[
/]
与表达式间有空格,变量加引号)
2. 数值比较与字符串比较混淆
- 错误场景:使用
=
进行数值判断(如[ $a = 10 ]
实际执行字符串匹配) - 解决方案:数值比较用
-eq
等专用运算符,字符串比较用=
,严格区分数据类型。
3. case 模式匹配的引号缺失
- 错误示例:
case $var in start) ... ;;
(未加引号,变量为空时匹配失败) - 最佳实践:始终为变量添加双引号(
"$var"
),确保空值或含空格的字符串正确匹配。
4. 状态码理解误区
- 错误逻辑:认为
$?=0
仅表示 “条件成立”,实际是 “前一命令执行成功” - 正确认知:条件测试的返回值与命令执行状态码一致,需结合具体命令(如
test
/[ ]
/ 实际服务命令)判断。