1. Shell脚本中的条件测试基础
在Shell脚本编程中,条件测试是控制程序流程的核心机制。不同于其他编程语言使用布尔值直接判断,Shell通过退出状态码(Exit Status)来评估条件是否成立。每个命令执行后都会返回0-255之间的状态码,其中0表示成功(true),非0表示失败(false)。
1.1 test命令与方括号语法
最基础的测试命令是test,它有一组丰富的参数用于各种比较:
bash复制test -f /etc/passwd # 测试文件是否存在
echo $? # 查看上条命令的退出状态
更常见的写法是使用方括号语法(注意空格是必须的):
bash复制[ -f /etc/passwd ] # 功能同上但更易读
现代脚本推荐使用双中括号[[ ]],它支持更多特性且更安全:
bash复制[[ -f /etc/passwd ]] # 防止变量扩展带来的问题
注意:单中括号是POSIX标准,双中括号是bash扩展特性。如果考虑跨平台兼容性需要谨慎选择。
1.2 常见测试操作符
文件测试操作符:
-e:文件存在(不区分类型)-f:是普通文件-d:是目录-r/-w/-x:可读/可写/可执行-s:文件存在且不为空-nt/-ot:文件新旧比较
字符串测试:
-z:字符串为空-n:字符串非空=/==:字符串相等!=:字符串不等
数值比较:
-eq/-ne:等于/不等于-gt/-lt:大于/小于-ge/-le:大于等于/小于等于
2. 条件分支结构详解
2.1 if-then-else基础结构
基本语法格式:
bash复制if 条件测试; then
# 条件为真时执行的代码
elif 其他条件; then
# 备选条件
else
# 所有条件都不满足时执行
fi
实际案例:检查用户是否为root
bash复制if [[ $UID -eq 0 ]]; then
echo "Running as root"
else
echo "Please run as root" >&2
exit 1
fi
2.2 case多分支结构
适用于有多个固定选项的场景,比多个if-elif更清晰:
bash复制case $变量 in
模式1)
命令序列
;;
模式2)
命令序列
;;
*)
默认命令
;;
esac
实际应用:处理命令行参数
bash复制case $1 in
start)
start_service
;;
stop)
stop_service
;;
restart)
restart_service
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
3. 高级条件测试技巧
3.1 组合条件测试
使用逻辑运算符连接多个条件:
&&:逻辑与(AND)||:逻辑或(OR)!:逻辑非(NOT)
双中括号内可以直接使用这些运算符:
bash复制[[ -f "$file" && -r "$file" ]] # 文件存在且可读
单中括号需要使用-a(and)/-o(or):
bash复制[ -f "$file" -a -r "$file" ]
3.2 命令组合测试
利用命令的退出状态进行条件判断:
bash复制if grep -q "error" /var/log/syslog; then
echo "Found errors in syslog"
fi
3.3 算术条件测试
双括号(( ))专门用于算术运算:
bash复制if (( $# < 2 )); then
echo "Need at least 2 arguments"
fi
4. 实战应用与调试技巧
4.1 配置文件检查案例
完整的安全检查脚本示例:
bash复制#!/bin/bash
config_file="/etc/app.conf"
if [[ ! -e "$config_file" ]]; then
echo "Config file missing" >&2
exit 1
elif [[ ! -r "$config_file" ]]; then
echo "Cannot read config file" >&2
exit 1
elif [[ $(stat -c %a "$config_file") != 600 ]]; then
echo "Incorrect file permissions" >&2
exit 1
else
source "$config_file"
fi
4.2 常见问题排查
- 变量未加引号导致空变量问题:
bash复制# 错误示范
if [ $var = "value" ] # 当$var为空时会变成 [ = "value" ]
# 正确做法
if [ "$var" = "value" ]
- 字符串比较使用单等号:
bash复制# 在[]中使用单个=
if [ "$os" = "Linux" ]
# 在[[]]中可以使用==
if [[ $os == "Linux" ]]
- 文件测试路径包含空格:
bash复制# 必须加引号
if [ -f "$my file" ]
4.3 性能优化建议
- 将高频判断放在前面:
bash复制# 更高效的顺序
if [[ $count -gt 10 ]]; then
# 高频情况
elif [[ $count -gt 5 ]]; then
# 次高频
else
# 低频
fi
- 避免不必要的子shell:
bash复制# 低效写法
if [[ $(whoami) = "root" ]]
# 更高效
if [[ $UID -eq 0 ]]
5. 现代Shell脚本最佳实践
5.1 使用set命令增强健壮性
在脚本开头添加:
bash复制set -euo pipefail
-e:命令失败时立即退出-u:使用未定义变量时报错-o pipefail:管道中任意命令失败则整个管道失败
5.2 条件测试风格建议
- 统一使用双中括号
[[ ]](bash环境下) - 变量比较始终加引号
- 数值比较使用
-eq等操作符而非算术扩展 - 复杂条件适当换行提高可读性:
bash复制if [[ $condition1 ]] \
&& [[ $condition2 ]] \
|| [[ $condition3 ]]; then
# ...
fi
5.3 错误处理模式
推荐的处理方式:
bash复制if ! do_something; then
echo "Error occurred" >&2
# 可以记录日志或发送告警
exit 1
fi
对于关键操作,可以使用trap捕获信号:
bash复制cleanup() {
# 清理临时文件等
}
trap cleanup EXIT ERR