本章导语:条件判断是程序逻辑控制的核心,Shell 脚本通过丰富的条件判断语法来处理各种复杂的业务逻辑。本章将系统介绍 Shell 中的 if/else/case 语句,深入讲解文件测试、字符串比较、数值判断等各类测试操作,帮助你在实际工作中灵活运用分支逻辑解决90%的业务问题。
学习目标
完成本章学习后,你将能够:
- 🎯 掌握 if/else/case 语句的完整语法结构
- 📁 熟练使用文件测试操作符检查文件状态
- 🔤 精通字符串和数值的多种比较方式
- 🧠 理解并运用逻辑运算符构建复合条件
- 🛡️ 避免条件判断中的常见错误和陷阱
- 🔧 编写出健壮、可读性强的分支逻辑
- 📋 根据不同场景选择最合适的判断方式
核心概念
- 分支结构:if 语句、case 语句、条件嵌套
- 测试操作:文件测试、字符串测试、数值测试
- 逻辑运算:AND、OR、NOT 运算符
- 条件表达式:test 命令、[ ]、[[ ]]、(( ))
应用场景
- 文件处理:检查文件存在性、权限、大小、时间等
- 用户验证:权限检查、身份认证、输入验证
- 系统监控:资源状态检查、阈值告警、服务检测
- 流程控制:根据不同条件执行不同操作、错误处理
3.1 条件判断的基础语法
if 语句基本结构
bash
#!/bin/bash
# 基本的 if 语句
number=10
if [ $number -gt 5 ]; then
echo "数字 $number 大于 5"
fi
# if-else 语句
age=18
if [ $age -ge 18 ]; then
echo "成年人"
else
echo "未成年人"
fi
# if-elif-else 语句
score=85
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "不及格"
fi
# 复合条件判断
file="/etc/passwd"
if [ -f "$file" ] && [ -r "$file" ]; then
echo "文件 $file 存在且可读"
else
echo "文件 $file 不存在或不可读"
fi
条件测试的不同语法
bash
#!/bin/bash
# test 命令(传统方式)
var="hello"
if test "$var" = "hello"; then
echo "使用 test 命令:匹配成功"
fi
# 方括号 [ ](最常用)
if [ "$var" = "hello" ]; then
echo "使用方括号:匹配成功"
fi
# 双方括号 [[ ]](推荐,更安全)
if [[ "$var" == "hello" ]]; then
echo "使用双方括号:匹配成功"
fi
# (( )) 用于算术运算
num=10
if (( num > 5 )); then
echo "使用算术运算:$num > 5"
fi
# 组合示例
string_var="Hello World"
number_var=42
# 传统方式(需要注意转义)
if [ "$string_var" = "Hello World" ] && [ $number_var -gt 40 ]; then
echo "传统方式:条件满足"
fi
# 双方括号方式(更安全,支持正则表达式)
if [[ "$string_var" == "Hello World" && $number_var -gt 40 ]]; then
echo "双方括号方式:条件满足"
fi
# 算术比较
if (( number_var > 40 && number_var < 50 )); then
echo "算术比较:$number_var 在 40-50 之间"
fi
条件判断的嵌套
bash
#!/bin/bash
# 嵌套 if 语句示例
user="admin"
age=25
permission="write"
# 基本嵌套
if [ "$user" = "admin" ]; then
echo "用户是管理员"
if [ $age -ge 18 ]; then
echo "成年管理员"
if [ "$permission" = "write" ]; then
echo "具有写权限"
fi
fi
fi
# 使用逻辑运算符简化嵌套
if [ "$user" = "admin" ] && [ $age -ge 18 ] && [ "$permission" = "write" ]; then
echo "成年管理员,具有写权限"
else
echo "权限不足"
fi
# 更复杂的嵌套示例
check_user_permission() {
local username="$1"
local user_age="$2"
local user_role="$3"
if [ -z "$username" ]; then
echo "错误:用户名不能为空"
return 1
fi
if [ "$user_role" = "admin" ]; then
echo "管理员权限检查"
if [ $user_age -ge 21 ]; then
echo "完全管理员权限"
return 0
else
echo "年龄不足,限制管理员权限"
return 2
fi
elif [ "$user_role" = "user" ]; then
echo "普通用户权限检查"
if [ $user_age -ge 18 ]; then
echo "完全用户权限"
return 0
else
echo "未成年用户,限制权限"
return 3
fi
else
echo "未知用户角色: $user_role"
return 4
fi
}
# 测试权限检查
echo "=== 权限检查测试 ==="
check_user_permission "admin" 25 "admin"
check_user_permission "john" 17 "user"
check_user_permission "guest" 30 "guest"
3.2 文件测试运算符
文件类型检查
bash
#!/bin/bash
# 创建测试文件和目录
touch test_file.txt
mkdir test_directory
ln -s test_file.txt test_symlink
test_file="test_file.txt"
test_dir="test_directory"
test_link="test_symlink"
nonexistent="/nonexistent/file"
# 文件存在性检查
echo "=== 文件存在性检查 ==="
if [ -f "$test_file" ]; then
echo "✓ $test_file 是普通文件"
else
echo "✗ $test_file 不是普通文件"
fi
if [ -d "$test_dir" ]; then
echo "✓ $test_dir 是目录"
else
echo "✗ $test_dir 不是目录"
fi
if [ -L "$test_link" ]; then
echo "✓ $test_link 是符号链接"
else
echo "✗ $test_link 不是符号链接"
fi
if [ ! -e "$nonexistent" ]; then
echo "✓ $nonexistent 不存在"
fi
# 文件权限检查
echo ""
echo "=== 文件权限检查 ==="
# 先设置不同的权限
chmod 755 "$test_file"
chmod 644 "$test_file"
if [ -r "$test_file" ]; then
echo "✓ $test_file 可读"
fi
if [ -w "$test_file" ]; then
echo "✓ $test_file 可写"
fi
if [ -x "$test_file" ]; then
echo "✓ $test_file 可执行"
else
echo "✗ $test_file 不可执行"
fi
# 文件内容检查
echo ""echo "=== 文件内容检查 ==="
# 写入一些内容
echo "This is a test file." > "$test_file"
echo "Second line." >> "$test_file"
if [ -s "$test_file" ]; then
echo "✓ $test_file 不为空"
file_size=$(stat -f%z "$test_file" 2>/dev/null || stat -c%s "$test_file" 2>/dev/null)
echo " 文件大小: $file_size 字节"
fi
# 文件特殊属性检查
echo ""
echo "=== 文件特殊属性检查 ==="
if [ -b "/dev/sda" ]; then
echo "✓ /dev/sda 是块设备"
fi
if [ -c "/dev/null" ]; then
echo "✓ /dev/null 是字符设备"
fi
if [ -p "/dev/stdin" ]; then
echo "✓ /dev/stdin 是命名管道"
fi
# 文件新旧比较
echo ""
echo "=== 文件新旧比较 ==="
sleep 1
touch new_file.txt
if [ "$test_file" -nt "new_file.txt" ]; then
echo "$test_file 比 new_file.txt 新"
else
echo "$test_file 不比 new_file.txt 新"
fi
if [ "new_file.txt" -ot "$test_file" ]; then
echo "new_file.txt 比 $test_file 旧"
else
echo "new_file.txt 不比 $test_file 旧"
fi
# 清理测试文件
rm -f "$test_file" "$test_link"
rm -rf "$test_dir"
rm -f "new_file.txt"
文件操作实用函数
bash
#!/bin/bash
# file_operations.sh - 文件操作实用函数
# 安全的文件复制
safe_copy() {
local src="$1"
local dest="$2"
local backup="${3:-true}"
# 检查源文件
if [ ! -f "$src" ]; then
echo "错误: 源文件不存在: $src" >&2
return 1
fi
if [ ! -r "$src" ]; then
echo "错误: 源文件不可读: $src" >&2
return 1
fi
# 检查目标目录
local dest_dir=$(dirname "$dest")
if [ ! -d "$dest_dir" ]; then
echo "错误: 目标目录不存在: $dest_dir" >&2
return 1
fi
# 备份已存在的文件
if [ -f "$dest" ] && [ "$backup" = "true" ]; then
local backup_file="${dest}.backup.$(date +%Y%m%d_%H%M%S)"
echo "备份已存在的文件: $dest -> $backup_file"
cp "$dest" "$backup_file"
fi
# 复制文件
cp "$src" "$dest"
if [ $? -eq 0 ]; then
echo "文件复制成功: $src -> $dest"
return 0
else
echo "错误: 文件复制失败" >&2
return 1
fi
}
# 智能的文件移动
smart_move() {
local src="$1"
local dest="$2"
# 检查源文件/目录
if [ ! -e "$src" ]; then
echo "错误: 源不存在: $src" >&2
return 1
fi
# 检查目标是否已存在
if [ -e "$dest" ]; then
if [ -f "$dest" ] && [ -f "$src" ]; then
# 文件到文件,需要确认
echo "警告: 目标文件已存在: $dest"
read -p "是否覆盖? (y/n): " confirm
if [ "$confirm" != "y" ]; then
echo "操作已取消"
return 1
fi
elif [ -d "$dest" ] && [ -f "$src" ]; then
# 文件到目录,使用原文件名
dest="$dest/$(basename "$src")"
fi
fi
# 移动文件
mv "$src" "$dest"
if [ $? -eq 0 ]; then
echo "文件移动成功: $src -> $dest"
return 0
else
echo "错误: 文件移动失败" >&2
return 1
fi
}
# 目录同步函数
sync_directories() {
local src_dir="$1"
local dest_dir="$2"
local dry_run="${3:-false}"
# 检查源目录
if [ ! -d "$src_dir" ]; then
echo "错误: 源目录不存在: $src_dir" >&2
return 1
fi
# 创建目标目录
if [ ! -d "$dest_dir" ]; then
if [ "$dry_run" = "true" ]; then
echo "[DRY RUN] 将创建目录: $dest_dir"
else
mkdir -p "$dest_dir"
echo "创建目录: $dest_dir"
fi
fi
# 同步文件
find "$src_dir" -type f | while read -r src_file; do
# 计算相对路径
rel_path=${src_file#$src_dir/}
dest_file="$dest_dir/$rel_path"
# 创建目标子目录
dest_subdir=$(dirname "$dest_file")
if [ ! -d "$dest_subdir" ]; then
if [ "$dry_run" = "true" ]; then
echo "[DRY RUN] 将创建子目录: $dest_subdir"
else
mkdir -p "$dest_subdir"
fi
fi
# 检查是否需要复制
if [ ! -f "$dest_file" ] || [ "$src_file" -nt "$dest_file" ]; then
if [ "$dry_run" = "true" ]; then
echo "[DRY RUN] 将复制: $src_file -> $dest_file"
else
cp "$src_file" "$dest_file"
echo "复制: $rel_path"
fi
fi
done
echo "目录同步完成"
}
# 使用示例
echo "=== 文件操作测试 ==="
# 创建测试文件
echo "测试内容" > test_source.txt
# 测试安全复制
safe_copy "test_source.txt" "test_dest.txt"
# 测试智能移动
mkdir -p test_target_dir
smart_move "test_dest.txt" "test_target_dir/"
# 测试目录同步
mkdir -p src_dir/subdir
echo "内容1" > src_dir/file1.txt
echo "内容2" > src_dir/subdir/file2.txt
sync_directories "src_dir" "dest_dir"
# 清理
rm -f test_source.txt
rm -rf test_target_dir src_dir dest_dir
3.3 字符串比较操作
基本字符串比较
bash
#!/bin/bash
# 字符串基本比较
str1="hello"
str2="world"
str3="hello"
str4="Hello"
empty_str=""
# 相等比较(区分大小写)
echo "=== 相等比较 ==="
if [ "$str1" = "$str3" ]; then
echo "✓ '$str1' = '$str3'"
fi
if [ "$str1" != "$str2" ]; then
echo "✓ '$str1' != '$str2'"
fi
# 大小写敏感比较
if [ "$str1" != "$str4" ]; then
echo "✓ '$str1' != '$str4' (大小写不同)"
fi
# 字符串存在性检查
echo ""
echo "=== 存在性检查 ==="
if [ -n "$str1" ]; then
echo "✓ '$str1' 非空"
fi
if [ -z "$empty_str" ]; then
echo "✓ empty_str 为空"
fi
if [ -z "$undefined_var" ]; then
echo "✓ undefined_var 为空(未定义)"
fi
# 双方括号的高级字符串比较
echo ""
echo "=== 高级比较 ==="
# 模式匹配
if [[ "$str1" == h* ]]; then
echo "✓ '$str1' 以 'h' 开头"
fi
if [[ "$str2" == *d ]]; then
echo "✓ '$str2' 以 'd' 结尾"
fi
if [[ "$str1" == h*lo ]]; then
echo "✓ '$str1' 匹配模式 'h*lo'"
fi
# 正则表达式匹配(Bash 3.0+)
if [[ "$str2" =~ ^w.*d$ ]]; then
echo "✓ '$str2' 匹配正则表达式 '^w.*d$'"
fi
# 字符串长度比较
echo ""
echo "=== 长度比较 ==="
if [[ ${#str1} -eq 5 ]]; then
echo "✓ '$str1' 长度为 5"
fi
if [[ ${#str1} -lt 10 ]]; then
echo "✓ '$str1' 长度小于 10"
fi
字符串处理函数
bash
#!/bin/bash
# string_functions.sh - 字符串处理函数库
# 字符串相等比较(忽略大小写)
str_equals_ignore_case() {
local str1="$1"
local str2="$2"
[[ "${str1,,}" == "${str2,,}" ]]
}
# 字符串包含检查
str_contains() {
local haystack="$1"
local needle="$2"
[[ "$haystack" == *"$needle"* ]]
}
# 字符串前缀检查
str_starts_with() {
local string="$1"
local prefix="$2"
[[ "$string" == "$prefix"* ]]
}
# 字符串后缀检查
str_ends_with() {
local string="$1"
local suffix="$2"
[[ "$string" == *"$suffix" ]]
}
# 字符串是否为数字
str_is_number() {
local str="$1"
[[ "$str" =~ ^-?[0-9]+$ ]]
}
# 字符串是否为邮箱
str_is_email() {
local email="$1"
[[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}
# 字符串是否为URL
str_is_url() {
local url="$1"
[[ "$url" =~ ^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} ]]
}
# 字符串是否为IP地址
str_is_ip() {
local ip="$1"
local ip_regex='^([0-9]{1,3}\.){3}[0-9]{1,3}$'
if [[ ! "$ip" =~ $ip_regex ]]; then
return 1
fi
# 检查每个段都在0-255范围内
IFS='.' read -ra segments <<< "$ip"
for segment in "${segments[@]}"; do
if [[ $segment -gt 255 ]]; then
return 1
fi
done
return 0
}
# 字符串长度检查
str_length_check() {
local str="$1"
local min_len="${2:-0}"
local max_len="${3:-999999}"
local len=${#str}
if [[ $len -lt $min_len ]]; then
return 1
fi
if [[ $len -gt $max_len ]]; then
return 1
fi
return 0
}
# 字符串格式化检查
str_check_format() {
local str="$1"
local format="$2"
case "$format" in
"phone")
# 简单的电话号码格式
[[ "$str" =~ ^[0-9]{3}-[0-9]{4}-[0-9]{4}$ ]]
;;
"date")
# YYYY-MM-DD 格式
[[ "$str" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]
;;
"time")
# HH:MM:SS 格式
[[ "$str" =~ ^[0-9]{2}:[0-9]{2}:[0-9]{2}$ ]]
;;
"username")
# 用户名格式:字母开头,可包含字母数字下划线
[[ "$str" =~ ^[a-zA-Z][a-zA-Z0-9_]*$ ]]
;;
"password")
# 密码格式:至少8位,包含字母和数字
[[ ${#str} -ge 8 && "$str" =~ [a-zA-Z] && "$str" =~ [0-9] ]]
;;
*)
echo "错误: 未知格式 $format" >&2
return 1
;;
esac
}
# 字符串相似度检查(简单的字符匹配)
str_similarity() {
local str1="$1"
local str2="$2"
local threshold="${3:-0.5}"
if [[ "$str1" == "$str2" ]]; then
echo "1.0"
return 0
fi
local len1=${#str1}
local len2=${#str2}
local max_len=$((len1 > len2 ? len1 : len2))
# 计算相同字符的百分比
local common_chars=0
local i j
for ((i=0; i<len1; i++)); do
for ((j=0; j<len2; j++)); do
if [[ "${str1:$i:1}" == "${str2:$j:1}" ]]; then
((common_chars++))
break
fi
done
done
local similarity=$(echo "scale=2; $common_chars / $max_len" | bc -l)
echo "$similarity"
if (( $(echo "$similarity >= $threshold" | bc -l) )); then
return 0
else
return 1
fi
}
# 字符串验证综合函数
validate_string() {
local value="$1"
local field_name="$2"
local validation_rules="$3"
IFS=',' read -ra rules <<< "$validation_rules"
for rule in "${rules[@]}"; do
rule=$(echo "$rule" | xargs) # 去除空格
case "$rule" in
"required")
if [[ -z "$value" ]]; then
echo "错误: $field_name 不能为空" >&2
return 1
fi
;;
"email")
if ! str_is_email "$value"; then
echo "错误: $field_name 不是有效的邮箱格式" >&2
return 1
fi
;;
"number")
if ! str_is_number "$value"; then
echo "错误: $field_name 必须是数字" >&2
return 1
fi
;;
"url")
if ! str_is_url "$value"; then
echo "错误: $field_name 不是有效的URL格式" >&2
return 1
fi
;;
"ip")
if ! str_is_ip "$value"; then
echo "错误: $field_name 不是有效的IP地址格式" >&2
return 1
fi
;;
"min:"*)
local min_len=${rule#min:}
if [[ ${#value} -lt $min_len ]]; then
echo "错误: $field_name 长度不能少于 $min_len 个字符" >&2
return 1
fi
;;
"max:"*)
local max_len=${rule#max:}
if [[ ${#value} -gt $max_len ]]; then
echo "错误: $field_name 长度不能超过 $max_len 个字符" >&2
return 1
fi
;;
"format:"*)
local format=${rule#format:}
if ! str_check_format "$value" "$format"; then
echo "错误: $field_name 格式不正确" >&2
return 1
fi
;;
*)
echo "警告: 未知验证规则 $rule" >&2
;;
esac
done
return 0
}
# 使用示例
echo "=== 字符串验证测试 ==="
# 测试邮箱验证
if str_is_email "test@example.com"; then
echo "✓ test@example.com 是有效邮箱"
fi
if ! str_is_email "invalid-email"; then
echo "✗ invalid-email 不是有效邮箱"
fi
# 测试包含检查
if str_contains "Hello World" "World"; then
echo "✓ 'Hello World' 包含 'World'"
fi
# 测试相似度
similarity=$(str_similarity "hello" "hallo")
echo "hello 与 hallo 的相似度: $similarity"
# 测试综合验证
validate_string "user@example.com" "邮箱" "required,email,min:5,max:50"
validate_string "12345678" "密码" "required,min:8,format:password"
3.4 数值比较运算
基本数值比较
bash
#!/bin/bash
# 数值比较示例
num1=10
num2=20
num3=10
# 使用 test 或 [ ] 进行数值比较
echo "=== 基本数值比较 ==="
if [ $num1 -eq $num3 ]; then
echo "✓ $num1 = $num3"
fi
if [ $num1 -lt $num2 ]; then
echo "✓ $num1 < $num2"
fi
if [ $num2 -gt $num1 ]; then
echo "✓ $num2 > $num1"
fi
if [ $num1 -le $num3 ]; then
echo "✓ $num1 <= $num3"
fi
if [ $num2 -ge $num1 ]; then
echo "✓ $num2 >= $num1"
fi
if [ $num1 -ne $num2 ]; then
echo "✓ $num1 != $num2"
fi
# 使用 (( )) 进行算术运算和比较(推荐)
echo ""
echo "=== 算术运算和比较 ==="
if (( num1 == num3 )); then
echo "✓ $num1 = $num3 (算术运算)"
fi
if (( num1 < num2 )); then
echo "✓ $num1 < $num2 (算术运算)"
fi
if (( num2 > num1 )); then
echo "✓ $num2 > $num1 (算术运算)"
fi
# 复合条件
if (( num1 > 0 && num2 > 0 )); then
echo "✓ $num1 和 $num2 都大于 0"
fi
if (( num1 > 15 || num2 > 15 )); then
echo "✓ $num1 或 $num2 大于 15"
fi
# 数值范围检查
value=25
if (( value >= 0 && value <= 100 )); then
echo "✓ $value 在 0-100 范围内"
fi
# 奇偶数检查
if (( value % 2 == 0 )); then
echo "✓ $value 是偶数"
else
echo "✓ $value 是奇数"
fi
# 数学表达式
result=$(( value * 2 + 10 ))
echo "$value * 2 + 10 = $result"
if (( result > 50 )); then
echo "✓ 结果 $result 大于 50"
fi
数值处理函数
bash
#!/bin/bash
# numeric_functions.sh - 数值处理函数库
# 数值验证
is_number() {
local value="$1"
# 支持整数和浮点数
[[ "$value" =~ ^-?[0-9]+(\.[0-9]+)?$ ]]
}
is_integer() {
local value="$1"
[[ "$value" =~ ^-?[0-9]+$ ]]
}
is_positive() {
local value="$1"
is_number "$value" && (( $(echo "$value > 0" | bc -l) ))
}
is_negative() {
local value="$1"
is_number "$value" && (( $(echo "$value < 0" | bc -l) ))
}
is_zero() {
local value="$1"
is_number "$value" && (( $(echo "$value == 0" | bc -l) ))
}
# 数值范围检查
in_range() {
local value="$1"
local min="$2"
local max="$3"
if ! is_number "$value" || ! is_number "$min" || ! is_number "$max"; then
return 1
fi
(( $(echo "$value >= $min && $value <= $max" | bc -l) ))
}
# 比较两个数值
compare_numbers() {
local num1="$1"
local num2="$2"
if ! is_number "$num1" || ! is_number "$num2"; then
echo "错误: 参数必须是数字" >&2
return 1
fi
if (( $(echo "$num1 > $num2" | bc -l) )); then
echo ">"
return 1
elif (( $(echo "$num1 < $num2" | bc -l) )); then
echo "<"
return -1
else
echo "="
return 0
fi
}
# 绝对值
abs() {
local value="$1"
if is_number "$value"; then
if is_negative "$value"; then
echo "$value" | awk '{print -$1}'
else
echo "$value"
fi
fi
}
# 四舍五入
round() {
local value="$1"
local precision="${2:-0}"
if is_number "$value"; then
echo "$value" | awk -v prec="$precision" '{printf "%.*f\n", prec, $1 + ($1 < 0 ? -0.5 : 0.5)}'
fi
}
# 向上取整
ceil() {
local value="$1"
if is_number "$value"; then
echo "$value" | awk '{print ($1 == int($1)) ? $1 : int($1) + 1}'
fi
}
# 向下取整
floor() {
local value="$1"
if is_number "$value"; then
echo "$value" | awk '{print int($1)}'
fi
}
# 数值验证函数
validate_number() {
local value="$1"
local field_name="$2"
local validation_rules="$3"
IFS=',' read -ra rules <<< "$validation_rules"
for rule in "${rules[@]}"; do
rule=$(echo "$rule" | xargs) # 去除空格
case "$rule" in
"required")
if [[ -z "$value" ]]; then
echo "错误: $field_name 不能为空" >&2
return 1
fi
;;
"integer")
if ! is_integer "$value"; then
echo "错误: $field_name 必须是整数" >&2
return 1
fi
;;
"positive")
if ! is_positive "$value"; then
echo "错误: $field_name 必须是正数" >&2
return 1
fi
;;
"negative")
if ! is_negative "$value"; then
echo "错误: $field_name 必须是负数" >&2
return 1
fi
;;
"non-zero")
if is_zero "$value"; then
echo "错误: $field_name 不能为零" >&2
return 1
fi
;;
"min:"*)
local min_val=${rule#min:}
if ! is_number "$min_val"; then
echo "错误: 最小值必须是数字" >&2
return 1
fi
if ! (( $(echo "$value >= $min_val" | bc -l) )); then
echo "错误: $field_name 不能小于 $min_val" >&2
return 1
fi
;;
"max:"*)
local max_val=${rule#max:}
if ! is_number "$max_val"; then
echo "错误: 最大值必须是数字" >&2
return 1
fi
if ! (( $(echo "$value <= $max_val" | bc -l) )); then
echo "错误: $field_name 不能大于 $max_val" >&2
return 1
fi
;;
"range:"*)
local range=${rule#range:}
IFS='-' read -ra range_parts <<< "$range"
local min_range=${range_parts[0]}
local max_range=${range_parts[1]}
if ! in_range "$value" "$min_range" "$max_range"; then
echo "错误: $field_name 必须在 $min_range 到 $max_range 之间" >&2
return 1
fi
;;
*)
echo "警告: 未知验证规则 $rule" >&2
;;
esac
done
return 0
}
# 统计函数
calculate_stats() {
local numbers=("$@")
local count=${#numbers[@]}
if [ $count -eq 0 ]; then
echo "错误: 至少需要一个数字" >&2
return 1
fi
# 验证所有输入都是数字
for num in "${numbers[@]}"; do
if ! is_number "$num"; then
echo "错误: $num 不是有效的数字" >&2
return 1
fi
done
# 计算总和
local sum=0
for num in "${numbers[@]}"; do
sum=$(echo "$sum + $num" | bc -l)
done
# 计算平均值
local average=$(echo "scale=6; $sum / $count" | bc -l)
# 找出最小值和最大值
local min="${numbers[0]}"
local max="${numbers[0]}"
for num in "${numbers[@]}"; do
if (( $(echo "$num < $min" | bc -l) )); then
min="$num"
fi
if (( $(echo "$num > $max" | bc -l) )); then
max="$num"
fi
done
echo "=== 统计结果 ==="
echo "数据: ${numbers[*]}"
echo "数量: $count"
echo "总和: $sum"
echo "平均值: $average"
echo "最小值: $min"
echo "最大值: $max"
echo "范围: $(echo "$max - $min" | bc -l)"
}
# 使用示例
echo "=== 数值处理测试 ==="
# 测试基本验证
if is_number "123.45"; then
echo "✓ 123.45 是有效数字"
fi
if is_integer "123"; then
echo "✓ 123 是整数"
fi
# 测试范围检查
if in_range "25" "0" "100"; then
echo "✓ 25 在 0-100 范围内"
fi
# 测试比较
result=$(compare_numbers "10.5" "8.3")
echo "10.5 与 8.3 的比较结果: $result"
# 测试数值验证
validate_number "25.5" "年龄" "required,positive,min:0,max:120"
validate_number "-5" "温度" "required,range:-100,100"
# 测试统计函数
calculate_stats 10 20 30 40 50
calculate_stats 1.5 2.7 3.1 4.8
3.5 复合条件与逻辑运算
逻辑运算符
bash
#!/bin/bash
# 逻辑运算符示例
age=25
has_permission=true
is_admin=false
file_exists=true
file_readable=true
# AND 运算 (使用 -a 或 &&)
echo "=== AND 运算 ==="
# 使用 -a(传统方式)
if [ $age -ge 18 -a "$has_permission" = true ]; then
echo "✓ 年龄 >= 18 且有权限 (使用 -a)"
fi
# 使用 &&(推荐方式)
if [ $age -ge 18 ] && [ "$has_permission" = true ]; then
echo "✓ 年龄 >= 18 且有权限 (使用 &&)"
fi
# 在双方括号中使用 &&
if [[ $age -ge 18 && "$has_permission" = true ]]; then
echo "✓ 年龄 >= 18 且有权限 (双方括号)"
fi
# OR 运算 (使用 -o 或 ||)
echo ""
echo "=== OR 运算 ==="
# 使用 -o(传统方式)
if [ "$is_admin" = true -o $age -ge 21 ]; then
echo "✓ 是管理员或年龄 >= 21 (使用 -o)"
fi
# 使用 ||(推荐方式)
if [ "$is_admin" = true ] || [ $age -ge 21 ]; then
echo "✓ 是管理员或年龄 >= 21 (使用 ||)"
fi
# NOT 运算 (使用 !)
echo ""
echo "=== NOT 运算 ==="
if [ ! "$is_admin" = true ]; then
echo "✓ 不是管理员"
fi
if [[ ! "$is_admin" = true && $age -ge 18 ]]; then
echo "✓ 不是管理员但年龄 >= 18"
fi
# 复杂条件组合
echo ""
echo "=== 复杂条件组合 ==="
# 文件权限检查
if [ "$file_exists" = true ] && [ "$file_readable" = true ]; then
echo "✓ 文件存在且可读"
fi
# 复杂的逻辑表达式
if [[ ($age -ge 18 && "$has_permission" = true) || "$is_admin" = true ]]; then
echo "✓ 满足访问条件: (成年且有权限) 或是管理员"
fi
# 算术运算中的逻辑
num=50
if (( num > 0 && num < 100 && num % 2 == 0 )); then
echo "✓ $num 是 0-100 之间的偶数"
fi
# 条件表达式简化
is_valid_user=false
# 简化前
if [ "$is_admin" = true ] || [ "$is_valid_user" = true ]; then
echo "有权限访问"
fi
# 简化后
if [[ "$is_admin" = true || "$is_valid_user" = true ]]; then
echo "有权限访问(简化版)"
fi
复合条件判断函数
bash
#!/bin/bash
# complex_conditions.sh - 复合条件判断函数库
# 通用条件判断函数
check_conditions() {
local operator="$1"
shift
local conditions=("$@")
case "$operator" in
"AND")
for condition in "${conditions[@]}"; do
if ! eval "$condition"; then
return 1
fi
done
return 0
;;
"OR")
for condition in "${conditions[@]}"; do
if eval "$condition"; then
return 0
fi
done
return 1
;;
*)
echo "错误: 未知操作符 $operator" >&2
return 1
;;
esac
}
# 多条件验证函数
validate_multiple_conditions() {
local data="$1"
local conditions_file="$2"
if [ ! -f "$conditions_file" ]; then
echo "错误: 条件文件不存在: $conditions_file" >&2
return 1
fi
while IFS= read -r condition; do
# 跳过空行和注释
[[ -z "$condition" || "$condition" =~ ^[[:space:]]*# ]] && continue
# 替换变量占位符
expanded_condition=$(echo "$condition" | sed "s/\$data/$data/g")
echo "检查条件: $expanded_condition"
if ! eval "$expanded_condition"; then
echo "✗ 条件检查失败: $condition"
return 1
fi
echo "✓ 条件检查通过"
done < "$conditions_file"
return 0
}
# 文件系统条件检查
check_file_conditions() {
local file_path="$1"
local required_perms="$2"
local max_size="${3:-}"
# 文件存在性检查
if [ ! -e "$file_path" ]; then
echo "✗ 文件不存在: $file_path"
return 1
fi
echo "✓ 文件存在: $file_path"
# 权限检查
IFS=',' read -ra perms <<< "$required_perms"
for perm in "${perms[@]}"; do
perm=$(echo "$perm" | xargs) # 去除空格
case "$perm" in
"read")
if [ ! -r "$file_path" ]; then
echo "✗ 文件不可读: $file_path"
return 1
fi
echo "✓ 文件可读"
;;
"write")
if [ ! -w "$file_path" ]; then
echo "✗ 文件不可写: $file_path"
return 1
fi
echo "✓ 文件可写"
;;
"execute")
if [ ! -x "$file_path" ]; then
echo "✗ 文件不可执行: $file_path"
return 1
fi
echo "✓ 文件可执行"
;;
*)
echo "警告: 未知权限要求: $perm" >&2
;;
esac
done
# 文件大小检查
if [ -n "$max_size" ]; then
local file_size=$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null)
if [ $file_size -gt "$max_size" ]; then
echo "✗ 文件过大: $file_size > $max_size 字节"
return 1
fi
echo "✓ 文件大小合适: $file_size 字节"
fi
return 0
}
# 网络连接条件检查
check_network_conditions() {
local host="$1"
local port="${2:-}"
local timeout="${3:-5}"
# 主机可达性检查
if ! ping -c 1 -W "$timeout" "$host" >/dev/null 2>&1; then
echo "✗ 主机不可达: $host"
return 1
fi
echo "✓ 主机可达: $host"
# 端口检查(如果指定了端口)
if [ -n "$port" ]; then
if ! timeout "$timeout" bash -c "</dev/tcp/$host/$port" 2>/dev/null; then
echo "✗ 端口不可达: $host:$port"
return 1
fi
echo "✓ 端口可达: $host:$port"
fi
return 0
}
# 系统资源条件检查
check_system_conditions() {
local max_cpu="${1:-80}"
local max_mem="${2:-80}"
local max_disk="${3:-90}"
# CPU 使用率检查
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//')
cpu_usage=${cpu_usage%.*} # 取整数部分
if [ $cpu_usage -gt "$max_cpu" ]; then
echo "✗ CPU 使用率过高: ${cpu_usage}% > ${max_cpu}%"
return 1
fi
echo "✓ CPU 使用率正常: ${cpu_usage}%"
# 内存使用率检查
local mem_usage=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}')
if [ $mem_usage -gt "$max_mem" ]; then
echo "✗ 内存使用率过高: ${mem_usage}% > ${max_mem}%"
return 1
fi
echo "✓ 内存使用率正常: ${mem_usage}%"
# 磁盘使用率检查
local disk_usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$disk_usage" -gt "$max_disk" ]; then
echo "✗ 磁盘使用率过高: ${disk_usage}% > ${max_disk}%"
return 1
fi
echo "✓ 磁盘使用率正常: ${disk_usage}%"
return 0
}
# 用户权限条件检查
check_user_permissions() {
local username="$1"
local required_group="$2"
local min_uid="${3:-1000}"
# 检查用户是否存在
if ! id "$username" >/dev/null 2>&1; then
echo "✗ 用户不存在: $username"
return 1
fi
echo "✓ 用户存在: $username"
# 检查 UID
local user_uid=$(id -u "$username")
if [ "$user_uid" -lt "$min_uid" ]; then
echo "✗ 用户 UID 过低: $user_uid < $min_uid"
return 1
fi
echo "✓ 用户 UID 合适: $user_uid"
# 检查用户组
if [ -n "$required_group" ]; then
if ! id -Gn "$username" | grep -q "$required_group"; then
echo "✗ 用户不在指定组中: $required_group"
return 1
fi
echo "✓ 用户属于指定组: $required_group"
fi
return 0
}
# 复合条件判断示例
main() {
echo "=== 复合条件判断测试 ==="
# 测试通用条件判断
age=25
has_permission=true
if check_conditions "AND" "[ $age -ge 18 ]" '[ "$has_permission" = true ]'; then
echo "✓ 通用条件检查通过: AND"
fi
if check_conditions "OR" '[ "$age" -lt 18 ]' '[ "$has_permission" = true ]'; then
echo "✓ 通用条件检查通过: OR"
fi
# 测试文件条件检查
echo ""
echo "=== 文件条件检查 ==="
# 创建测试文件
test_file="/tmp/test_conditions.txt"
echo "测试内容" > "$test_file"
chmod 644 "$test_file"
if check_file_conditions "$test_file" "read,write" "1000"; then
echo "✓ 文件条件检查全部通过"
fi
# 测试网络条件检查
echo ""
echo "=== 网络条件检查 ==="
if check_network_conditions "8.8.8.8" "53" "3"; then
echo "✓ 网络条件检查通过"
fi
# 测试系统条件检查
echo ""
echo "=== 系统条件检查 ==="
if check_system_conditions "95" "95" "95"; then
echo "✓ 系统条件检查通过"
else
echo "系统资源使用率较高"
fi
# 测试用户权限检查
echo ""
echo "=== 用户权限检查 ==="
if check_user_permissions "$(whoami)" "" "1000"; then
echo "✓ 用户权限检查通过"
fi
# 清理
rm -f "$test_file"
}
# 执行主函数
main "$@"
3.6 case 语句的多分支处理
case 语句基本用法
bash
#!/bin/bash
# case 语句基本示例
fruit="apple"
echo "=== 基本 case 语句 ==="
case "$fruit" in
"apple")
echo "这是一个苹果"
;;
"banana")
echo "这是一个香蕉"
;;
"orange")
echo "这是一个橙子"
;;
*)
echo "未知水果: $fruit"
;;
esac
# 模式匹配
echo ""
echo "=== 模式匹配 ==="
filename="document.pdf"
case "$filename" in
*.txt)
echo "文本文件: $filename"
;;
*.pdf)
echo "PDF 文件: $filename"
;;
*.jpg|*.jpeg|*.png)
echo "图片文件: $filename"
;;
*.zip|*.tar|*.gz)
echo "压缩文件: $filename"
;;
*)
echo "其他文件: $filename"
;;
esac
# 多个值匹配同一个模式
echo ""
echo "=== 多值匹配 ==="
day="Monday"
case "$day" in
Monday|Tuesday|Wednesday|Thursday|Friday)
echo "工作日: $day"
;;
Saturday|Sunday)
echo "周末: $day"
;;
*)
echo "无效的日期: $day"
;;
esac
# 数字范围匹配
echo ""
echo "=== 数字范围匹配 ==="
score=75
case $score in
[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])
echo "不及格 (0-59): $score"
;;
6[0-9]|7[0-9])
echo "及格 (60-79): $score"
;;
8[0-9]|9[0-9]|100)
echo "优秀 (80-100): $score"
;;
*)
echo "无效分数: $score"
;;
esac
# 使用通配符
echo ""
echo "=== 通配符匹配 ==="
input="Hello World"
case "$input" in
H*)
echo "以 H 开头: $input"
;;
*d)
echo "以 d 结尾: $input"
;;
*W*)
echo "包含 W: $input"
;;
*)
echo "其他模式: $input"
;;
esac
case 语句高级应用
bash
#!/bin/bash
# case_statement_advanced.sh - case 语句高级应用
# 交互式菜单系统
show_menu() {
clear
echo "=============================="
echo " 系统管理菜单"
echo "=============================="
echo "1. 系统信息查看"
echo "2. 用户管理"
echo "3. 网络配置"
echo "4. 服务管理"
echo "5. 磁盘管理"
echo "6. 日志查看"
echo "7. 备份操作"
echo "q. 退出"
echo "=============================="
echo -n "请选择操作 [1-7,q]: "
}
# 系统信息查看
show_system_info() {
echo "=== 系统信息 ==="
echo "操作系统: $(uname -s) $(uname -r)"
echo "主机名: $(hostname)"
echo "当前用户: $(whoami)"
echo "系统时间: $(date)"
echo "系统负载: $(uptime | awk -F'load average:' '{print $2}')"
echo ""
}
# 用户管理
manage_users() {
echo "=== 用户管理 ==="
echo "当前在线用户:"
who
echo ""
echo "用户列表:"
awk -F: '$7 != "/nologin" {print $1 " (UID:" $3 ")"}' /etc/passwd
echo ""
}
# 网络配置
network_config() {
echo "=== 网络配置 ==="
echo "网络接口:"
ip addr show | grep -E "^[0-9]+:|inet "
echo ""
echo "路由表:"
ip route | head -5
echo ""
}
# 服务管理
manage_services() {
echo "=== 服务管理 ==="
echo "主要服务状态:"
services=("sshd" "cron" "network" "firewalld" "nginx")
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null; then
status="运行中"
else
status="已停止"
fi
echo " $service: $status"
done
echo ""
}
# 磁盘管理
disk_management() {
echo "=== 磁盘管理 ==="
echo "磁盘使用情况:"
df -h
echo ""
echo "目录大小 (前5名):"
du -sh /* 2>/dev/null | sort -hr | head -5
echo ""
}
# 日志查看
view_logs() {
echo "=== 日志查看 ==="
echo "系统日志 (最近10行):"
tail -10 /var/log/syslog 2>/dev/null || tail -10 /var/log/messages 2>/dev/null
echo ""
echo "认证日志 (最近5行):"
tail -5 /var/log/auth.log 2>/dev/null || tail -5 /var/log/secure 2>/dev/null
echo ""
}
# 备份操作
backup_operations() {
echo "=== 备份操作 ==="
local backup_dir="/tmp/backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
echo "正在备份重要配置文件..."
# 备份配置文件
config_files=("/etc/passwd" "/etc/group" "/etc/fstab")
for config_file in "${config_files[@]}"; do
if [ -f "$config_file" ]; then
cp "$config_file" "$backup_dir/" 2>/dev/null && echo " 已备份: $config_file"
fi
done
echo "备份完成: $backup_dir"
echo ""
}
# 主菜单循环
main() {
while true; do
show_menu
read -r choice
case "$choice" in
1)
show_system_info
;;
2)
manage_users
;;
3)
network_config
;;
4)
manage_services
;;
5)
disk_management
;;
6)
view_logs
;;
7)
backup_operations
;;
[Qq]|[Qq][Uu][Ii][Tt])
echo "感谢使用,再见!"
exit 0
;;
*)
echo "无效选择: $choice"
echo "请输入 1-7 或 q"
;;
esac
echo -n "按 Enter 键继续..."
read -r
done
}
# 文件类型处理器
process_file() {
local file="$1"
case "$file" in
*.tar.gz|*.tgz)
echo "解压 tar.gz 文件: $file"
tar -xzf "$file"
;;
*.tar.bz2|*.tbz2)
echo "解压 tar.bz2 文件: $file"
tar -xjf "$file"
;;
*.zip)
echo "解压 zip 文件: $file"
unzip "$file"
;;
*.rar)
echo "解压 rar 文件: $file"
unrar x "$file"
;;
*.7z)
echo "解压 7z 文件: $file"
7z x "$file"
;;
*.gz)
echo "解压 gz 文件: $file"
gunzip "$file"
;;
*.bz2)
echo "解压 bz2 文件: $file"
bunzip2 "$file"
;;
*)
echo "不支持的文件格式: $file"
return 1
;;
esac
}
# 网络服务处理器
handle_service() {
local service="$1"
local action="$2"
case "$action" in
start)
echo "启动服务: $service"
sudo systemctl start "$service"
;;
stop)
echo "停止服务: $service"
sudo systemctl stop "$service"
;;
restart)
echo "重启服务: $service"
sudo systemctl restart "$service"
;;
status)
echo "查看服务状态: $service"
systemctl status "$service"
;;
enable)
echo "启用服务自启动: $service"
sudo systemctl enable "$service"
;;
disable)
echo "禁用服务自启动: $service"
sudo systemctl disable "$service"
;;
*)
echo "不支持的操作: $action"
echo "支持的操作: start, stop, restart, status, enable, disable"
return 1
;;
esac
}
# 参数解析器
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
echo "用法: $0 [选项]"
echo "选项:"
echo " -h, --help 显示帮助信息"
echo " -v, --verbose 详细输出"
echo " -f, --file 指定文件"
echo " -o, --output 指定输出文件"
exit 0
;;
-v|--verbose)
export VERBOSE=1
echo "启用详细输出"
shift
;;
-f|--file)
INPUT_FILE="$2"
echo "输入文件: $2"
shift 2
;;
-o|--output)
OUTPUT_FILE="$2"
echo "输出文件: $2"
shift 2
;;
*)
echo "未知选项: $1"
exit 1
;;
esac
done
}
# 错误码处理器
handle_error() {
local exit_code=$1
case $exit_code in
0)
echo "✓ 操作成功完成"
;;
1)
echo "✗ 一般错误"
;;
2)
echo "✗ 误用 Shell 内置命令"
;;
126)
echo "✗ 命令不可执行"
;;
127)
echo "✗ 命令未找到"
;;
130)
echo "✗ 脚本被用户中断"
;;
*)
echo "✗ 未知错误码: $exit_code"
;;
esac
}
# 使用示例
echo "=== case 语句高级应用演示 ==="
# 演示参数解析
echo "1. 参数解析演示:"
parse_arguments --verbose --file "input.txt" --output "output.txt"
echo ""
echo "2. 错误码处理演示:"
handle_error 0
handle_error 127
echo ""
echo "3. 文件类型处理演示:"
echo "模拟处理不同类型的压缩文件..."
process_file "archive.tar.gz"
process_file "document.zip"
# 如果以脚本方式运行,显示菜单
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo ""
echo "4. 交互式菜单演示 (按 Ctrl+C 退出):"
# main
fi
本章总结
本章详细讲解了 Shell 条件判断的各种语法和应用场景,从基础的 if/else 语句到复杂的 case 语句,涵盖了文件测试、字符串比较、数值运算和逻辑运算等核心内容。
核心要点:
- 基础语法掌握:理解 if/elif/else、case 语句的基本结构和应用场景
- 测试命令选择 :合理使用
[ ]、[[ ]]、test和(( ))等不同的测试方式 - 文件操作检查:熟练运用文件测试运算符进行文件存在性、权限、类型等检查
- 字符串处理技巧:掌握字符串比较、模式匹配、正则表达式等高级技巧
- 数值运算处理:学会使用数值比较运算符和算术表达式进行数学判断
- 逻辑运算组合:理解 AND、OR、NOT 逻辑运算符的使用和优先级
- 复杂条件构建:能够构建多层嵌套和复合条件的判断逻辑
最佳实践:
- 优先使用
[[ ]]进行条件测试,更安全且功能更丰富 - 对包含空格或特殊字符的变量总是使用引号
- 将复杂条件分解为简单的子条件,提高可读性
- 使用函数封装重复的条件检查逻辑
- 对用户输入进行严格的验证和检查
- 使用 case 语句处理多分支情况,提高代码清晰度
实践练习
-
创建一个系统监控脚本,要求:
- 检查 CPU、内存、磁盘使用率
- 根据不同阈值显示不同警告级别
- 使用 if/elif/else 结构处理不同情况
-
实现一个文件批处理工具:
- 根据文件扩展名执行不同操作
- 使用 case 语句处理多种文件类型
- 包含错误处理和用户确认机制
-
编写一个用户认证脚本:
- 验证用户名、密码、邮箱格式
- 使用复合条件检查多个输入
- 提供详细的错误信息和修改建议