条件判断是Shell脚本编程中实现逻辑控制的核心功能。通过条件判断,我们可以根据不同的情况执行不同的代码块,使脚本具有智能决策能力。
最基本的条件判断结构,支持多种条件测试和分支。
用于多分支选择,特别适合模式匹配的场景。
用于测试文件属性、字符串比较和数值比较。
检查文件是否存在、可读、可写、可执行等属性。
比较字符串是否相等、为空、匹配模式等。
比较数值的大小、相等关系,支持算术运算。
条件判断是Shell脚本实现自动化、智能化的关键。通过合理的条件判断,脚本可以根据运行环境、用户输入、文件状态等因素做出正确的决策,大大提高脚本的实用性和健壮性。
if语句是最基本也是最常用的条件判断结构。
#!/bin/bash
# if语句示例
echo "=== 基本if语句 ==="
number=10
if [ $number -gt 5 ]; then
echo "数字 $number 大于 5"
fi
echo -e "\n=== if-else语句 ==="
filename="test.txt"
if [ -f "$filename" ]; then
echo "文件 $filename 存在"
else
echo "文件 $filename 不存在"
# 可以在这里创建文件或执行其他操作
fi
echo -e "\n=== if-elif-else语句 ==="
score=85
if [ $score -ge 90 ]; then
echo "成绩优秀"
elif [ $score -ge 80 ]; then
echo "成绩良好"
elif [ $score -ge 70 ]; then
echo "成绩中等"
elif [ $score -ge 60 ]; then
echo "成绩及格"
else
echo "成绩不及格"
fi
echo -e "\n=== 嵌套if语句 ==="
age=25
has_license=true
if [ $age -ge 18 ]; then
echo "年龄符合要求"
if [ "$has_license" = true ]; then
echo "有驾驶执照,可以驾驶"
else
echo "没有驾驶执照,不能驾驶"
fi
else
echo "年龄不符合要求"
fi
echo -e "\n=== 使用双括号进行数值比较 ==="
a=15
b=20
if (( a > b )); then
echo "$a 大于 $b"
else
echo "$a 不大于 $b"
fi
# 复合条件
if (( a > 10 && b > 15 )); then
echo "a 大于 10 且 b 大于 15"
fi
echo -e "\n=== 使用双中括号进行字符串比较 ==="
name="Alice"
if [[ "$name" == "Alice" ]]; then
echo "你好, Alice!"
elif [[ "$name" == "Bob" ]]; then
echo "你好, Bob!"
else
echo "你好, 陌生人!"
fi
# 模式匹配
filename="document.pdf"
if [[ "$filename" == *.pdf ]]; then
echo "这是一个PDF文件"
fi
echo -e "\n=== 命令执行结果判断 ==="
# 检查命令是否执行成功
if ls /nonexistent > /dev/null 2>&1; then
echo "命令执行成功"
else
echo "命令执行失败"
fi
# 使用命令输出作为条件
file_count=$(ls | wc -l)
if [ $file_count -gt 10 ]; then
echo "当前目录文件数量较多: $file_count"
else
echo "当前目录文件数量正常: $file_count"
fi
[[ ]]而不是[ ],功能更强大且更安全(( ))进行数值运算和比较test命令用于测试条件是否成立,可以用[ ]或[[ ]]代替。
| 运算符 | 说明 |
|---|---|
-e |
文件/目录是否存在 |
-f |
是否是普通文件 |
-d |
是否是目录 |
-r |
文件是否可读 |
-w |
文件是否可写 |
-x |
文件是否可执行 |
-s |
文件大小是否大于0 |
-L |
是否是符号链接 |
| 运算符 | 说明 |
|---|---|
= |
字符串相等 |
!= |
字符串不相等 |
-z |
字符串长度为0 |
-n |
字符串长度不为0 |
< |
字符串小于(字典序) |
> |
字符串大于(字典序) |
| 运算符 | 说明 |
|---|---|
-eq |
等于 |
-ne |
不等于 |
-gt |
大于 |
-lt |
小于 |
-ge |
大于等于 |
-le |
小于等于 |
| 运算符 | 说明 |
|---|---|
! |
逻辑非 |
-a |
逻辑与 |
-o |
逻辑或 |
&& |
逻辑与 |
|| |
逻辑或 |
#!/bin/bash
# test命令使用示例
echo "=== 文件测试 ==="
file="test.txt"
dir="/tmp"
# 创建测试文件
echo "Hello World" > "$file"
# 使用test命令
if test -e "$file"; then
echo "文件 $file 存在"
else
echo "文件 $file 不存在"
fi
# 使用 [ ] 代替test
if [ -f "$file" ]; then
echo "$file 是普通文件"
fi
if [ -d "$dir" ]; then
echo "$dir 是目录"
fi
if [ -r "$file" ]; then
echo "$file 可读"
fi
if [ -w "$file" ]; then
echo "$file 可写"
fi
if [ -x "$file" ]; then
echo "$file 可执行"
else
echo "$file 不可执行"
fi
echo -e "\n=== 字符串测试 ==="
str1="hello"
str2="world"
empty=""
if [ "$str1" = "$str2" ]; then
echo "字符串相等"
else
echo "字符串不相等"
fi
if [ "$str1" != "$str2" ]; then
echo "字符串不相等"
fi
if [ -z "$empty" ]; then
echo "字符串为空"
fi
if [ -n "$str1" ]; then
echo "字符串不为空: $str1"
fi
# 在 [[ ]] 中使用模式匹配
filename="document.pdf"
if [[ "$filename" == *.pdf ]]; then
echo "这是一个PDF文件"
fi
# 正则表达式匹配
email="user@example.com"
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "有效的邮箱地址"
else
echo "无效的邮箱地址"
fi
echo -e "\n=== 数值测试 ==="
a=10
b=20
if [ $a -eq $b ]; then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b : a 不等于 b"
fi
if [ $a -ne $b ]; then
echo "$a -ne $b : a 不等于 b"
fi
if [ $a -gt $b ]; then
echo "$a -gt $b : a 大于 b"
else
echo "$a -gt $b : a 不大于 b"
fi
if [ $a -lt $b ]; then
echo "$a -lt $b : a 小于 b"
else
echo "$a -lt $b : a 不小于 b"
fi
# 使用双括号进行数值比较
if (( a < b )); then
echo "((a < b)) : a 小于 b"
fi
echo -e "\n=== 逻辑运算 ==="
file1="file1.txt"
file2="file2.txt"
# 使用 -a 和 -o
if [ -f "$file1" -a -f "$file2" ]; then
echo "两个文件都存在"
else
echo "至少有一个文件不存在"
fi
if [ -f "$file1" -o -f "$file2" ]; then
echo "至少有一个文件存在"
else
echo "两个文件都不存在"
fi
# 使用 && 和 ||
if [ -f "$file1" ] && [ -f "$file2" ]; then
echo "两个文件都存在"
else
echo "至少有一个文件不存在"
fi
if [ -f "$file1" ] || [ -f "$file2" ]; then
echo "至少有一个文件存在"
else
echo "两个文件都不存在"
fi
# 使用 !
if [ ! -f "nonexistent.txt" ]; then
echo "文件不存在"
fi
echo -e "\n=== 复合条件 ==="
age=25
score=85
if [ $age -ge 18 ] && [ $score -ge 60 ]; then
echo "年龄合格且成绩及格"
fi
if [[ $age -ge 18 && $score -ge 60 ]]; then
echo "年龄合格且成绩及格"
fi
# 使用括号分组
if [ \( "$age" -ge 18 -a "$score" -ge 60 \) -o "$is_admin" = true ]; then
echo "年龄合格且成绩及格,或者是管理员"
fi
# 清理测试文件
rm -f "$file"
[ ] 是test命令的另一种写法,是POSIX标准[[ ]] 是Bash的扩展功能,更强大更安全[[ ]] 支持模式匹配和正则表达式[[ ]] 中的变量不需要引号[[ ]] 支持 && 和 || 运算符case语句用于多分支选择,特别适合模式匹配的场景。
#!/bin/bash
# case语句示例
echo "=== 基本case语句 ==="
fruit="apple"
case $fruit in
"apple")
echo "这是苹果"
;;
"banana")
echo "这是香蕉"
;;
"orange")
echo "这是橙子"
;;
*)
echo "未知水果"
;;
esac
echo -e "\n=== 模式匹配 ==="
filename="document.pdf"
case $filename in
*.txt)
echo "文本文件"
;;
*.pdf|*.doc|*.docx)
echo "文档文件"
;;
*.jpg|*.png|*.gif)
echo "图像文件"
;;
*.sh)
echo "Shell脚本"
;;
*)
echo "其他类型文件"
;;
esac
echo -e "\n=== 使用正则表达式模式 ==="
number=42
case $number in
[0-9]|[1-9][0-9])
echo "个位数或两位数"
;;
1[0-9]{2}|[2-9][0-9]{2})
echo "三位数"
;;
[1-9][0-9]{3,})
echo "四位数或更多"
;;
*)
echo "其他"
;;
esac
echo -e "\n=== 系统信息判断 ==="
os_type=$(uname -s)
case $os_type in
"Linux")
echo "这是Linux系统"
# 可以在这里添加Linux特定的命令
;;
"Darwin")
echo "这是macOS系统"
# 可以在这里添加macOS特定的命令
;;
"CYGWIN"*|"MINGW"*|"MSYS"*)
echo "这是Windows系统"
# 可以在这里添加Windows特定的命令
;;
*)
echo "未知操作系统: $os_type"
;;
esac
echo -e "\n=== 用户输入处理 ==="
read -p "请输入命令 (start|stop|restart|status): " command
case $command in
start|START)
echo "启动服务..."
# 启动服务的代码
;;
stop|STOP)
echo "停止服务..."
# 停止服务的代码
;;
restart|RESTART)
echo "重启服务..."
# 重启服务的代码
;;
status|STATUS)
echo "查看服务状态..."
# 查看状态的代码
;;
*)
echo "未知命令: $command"
echo "可用命令: start, stop, restart, status"
;;
esac
echo -e "\n=== 嵌套case语句 ==="
file_type="text"
extension="txt"
case $file_type in
"text")
case $extension in
"txt")
echo "普通文本文件"
;;
"md")
echo "Markdown文件"
;;
"html"|"htm")
echo "HTML文件"
;;
*)
echo "其他文本格式"
;;
esac
;;
"image")
case $extension in
"jpg"|"jpeg")
echo "JPEG图像"
;;
"png")
echo "PNG图像"
;;
"gif")
echo "GIF图像"
;;
*)
echo "其他图像格式"
;;
esac
;;
*)
echo "未知文件类型"
;;
esac
echo -e "\n=== 使用case进行错误处理 ==="
read -p "请输入文件名: " input_file
case $input_file in
"")
echo "错误: 文件名不能为空"
exit 1
;;
*[[:space:]]*)
echo "错误: 文件名不能包含空格"
exit 1
;;
*/*)
echo "错误: 文件名不能包含路径分隔符"
exit 1
;;
.|..)
echo "错误: 无效的文件名"
exit 1
;;
*)
echo "有效的文件名: $input_file"
# 处理文件的代码
;;
esac
* 匹配任意字符? 匹配单个字符[abc] 匹配a、b或c中的任意一个字符[a-z] 匹配a到z之间的任意字符| 用于分隔多个模式通过实际脚本示例展示Shell条件判断的综合应用。
使用条件判断实现系统资源监控和告警。
#!/bin/bash
# 系统监控脚本 - 条件判断综合示例
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 阈值定义
CPU_WARNING=80
CPU_CRITICAL=90
MEMORY_WARNING=80
MEMORY_CRITICAL=90
DISK_WARNING=80
DISK_CRITICAL=90
# 日志文件
LOG_FILE="/var/log/system_monitor.log"
# 记录日志
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
case $level in
"ERROR")
echo -e "${RED}[ERROR]${NC} $message"
;;
"WARNING")
echo -e "${YELLOW}[WARNING]${NC} $message"
;;
"INFO")
echo -e "${BLUE}[INFO]${NC} $message"
;;
"SUCCESS")
echo -e "${GREEN}[SUCCESS]${NC} $message"
;;
*)
echo "[$level] $message"
;;
esac
}
# 检查CPU使用率
check_cpu_usage() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
cpu_usage=${cpu_usage%.*}
if [ -z "$cpu_usage" ]; then
log_message "ERROR" "无法获取CPU使用率"
return 1
fi
log_message "INFO" "CPU使用率: ${cpu_usage}%"
if [ "$cpu_usage" -ge "$CPU_CRITICAL" ]; then
log_message "ERROR" "CPU使用率超过临界值: ${cpu_usage}% >= ${CPU_CRITICAL}%"
return 2
elif [ "$cpu_usage" -ge "$CPU_WARNING" ]; then
log_message "WARNING" "CPU使用率超过警告值: ${cpu_usage}% >= ${CPU_WARNING}%"
return 1
else
log_message "SUCCESS" "CPU使用率正常: ${cpu_usage}%"
return 0
fi
}
# 检查内存使用率
check_memory_usage() {
local memory_info=$(free | grep Mem)
local total_memory=$(echo $memory_info | awk '{print $2}')
local used_memory=$(echo $memory_info | awk '{print $3}')
local memory_usage=$((used_memory * 100 / total_memory))
log_message "INFO" "内存使用率: ${memory_usage}%"
if [ "$memory_usage" -ge "$MEMORY_CRITICAL" ]; then
log_message "ERROR" "内存使用率超过临界值: ${memory_usage}% >= ${MEMORY_CRITICAL}%"
return 2
elif [ "$memory_usage" -ge "$MEMORY_WARNING" ]; then
log_message "WARNING" "内存使用率超过警告值: ${memory_usage}% >= ${MEMORY_WARNING}%"
return 1
else
log_message "SUCCESS" "内存使用率正常: ${memory_usage}%"
return 0
fi
}
# 检查磁盘使用率
check_disk_usage() {
local disk_usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
log_message "INFO" "根分区使用率: ${disk_usage}%"
if [ "$disk_usage" -ge "$DISK_CRITICAL" ]; then
log_message "ERROR" "磁盘使用率超过临界值: ${disk_usage}% >= ${DISK_CRITICAL}%"
return 2
elif [ "$disk_usage" -ge "$DISK_WARNING" ]; then
log_message "WARNING" "磁盘使用率超过警告值: ${disk_usage}% >= ${DISK_WARNING}%"
return 1
else
log_message "SUCCESS" "磁盘使用率正常: ${disk_usage}%"
return 0
fi
}
# 检查服务状态
check_service() {
local service_name="$1"
if systemctl is-active --quiet "$service_name"; then
log_message "SUCCESS" "服务 $service_name 正在运行"
return 0
else
log_message "ERROR" "服务 $service_name 未运行"
return 1
fi
}
# 检查端口监听
check_port() {
local port="$1"
local service_name="$2"
if netstat -tuln | grep -q ":$port "; then
log_message "SUCCESS" "端口 $port ($service_name) 正在监听"
return 0
else
log_message "ERROR" "端口 $port ($service_name) 未监听"
return 1
fi
}
# 检查文件系统
check_filesystem() {
local mount_point="$1"
if mountpoint -q "$mount_point"; then
log_message "SUCCESS" "挂载点 $mount_point 正常"
return 0
else
log_message "ERROR" "挂载点 $mount_point 异常"
return 1
fi
}
# 主监控函数
main_monitor() {
local overall_status=0
echo -e "${BLUE}=== 系统监控开始 ===${NC}"
log_message "INFO" "系统监控开始"
# 检查CPU
if ! check_cpu_usage; then
case $? in
1) ((overall_status=overall_status|1)) ;; # 警告
2) ((overall_status=overall_status|2)) ;; # 错误
esac
fi
# 检查内存
if ! check_memory_usage; then
case $? in
1) ((overall_status=overall_status|1)) ;;
2) ((overall_status=overall_status|2)) ;;
esac
fi
# 检查磁盘
if ! check_disk_usage; then
case $? in
1) ((overall_status=overall_status|1)) ;;
2) ((overall_status=overall_status|2)) ;;
esac
fi
# 检查关键服务
check_service "sshd"
if [ $? -ne 0 ]; then
((overall_status=overall_status|2))
fi
check_service "crond"
if [ $? -ne 0 ]; then
((overall_status=overall_status|1))
fi
# 检查关键端口
check_port 22 "SSH"
if [ $? -ne 0 ]; then
((overall_status=overall_status|2))
fi
# 检查关键挂载点
check_filesystem "/"
if [ $? -ne 0 ]; then
((overall_status=overall_status|2))
fi
# 根据总体状态决定退出码
case $overall_status in
0)
log_message "SUCCESS" "所有检查项正常"
echo -e "${GREEN}=== 所有检查项正常 ===${NC}"
;;
1)
log_message "WARNING" "存在警告项,需要关注"
echo -e "${YELLOW}=== 存在警告项,需要关注 ===${NC}"
;;
2|3)
log_message "ERROR" "存在错误项,需要立即处理"
echo -e "${RED}=== 存在错误项,需要立即处理 ===${NC}"
;;
esac
log_message "INFO" "系统监控结束"
echo -e "${BLUE}=== 系统监控结束 ===${NC}"
return $overall_status
}
# 参数处理
case "${1:-}" in
"monitor")
main_monitor
;;
"status")
# 简略状态检查
check_cpu_usage > /dev/null
check_memory_usage > /dev/null
check_disk_usage > /dev/null
;;
"log")
# 显示日志
if [ -f "$LOG_FILE" ]; then
tail -20 "$LOG_FILE"
else
echo "日志文件不存在: $LOG_FILE"
fi
;;
"install")
# 安装为cron任务
echo "安装系统监控cron任务..."
(crontab -l 2>/dev/null; echo "*/5 * * * * $(pwd)/$(basename "$0") monitor") | crontab -
echo "安装完成"
;;
"help"|"-h"|"--help")
echo "用法: $0 [command]"
echo "命令:"
echo " monitor 执行完整监控检查"
echo " status 执行简略状态检查"
echo " log 显示监控日志"
echo " install 安装为cron任务"
echo " help 显示帮助信息"
;;
*)
main_monitor
;;
esac
./system_monitor.sh monitor - 执行完整监控检查./system_monitor.sh status - 执行简略状态检查./system_monitor.sh log - 查看监控日志./system_monitor.sh install - 安装为定时任务
[[ ]] 进行字符串和模式匹配(( )) 进行数值运算和比较使用 set -x 开启调试模式查看条件判断的执行过程。在关键条件前后添加 echo 语句输出变量值和判断结果。使用 bash -n script.sh 检查语法错误。