| 知乎专栏 |
test 命令用于条件判断,返回 0(真)或非 0(假)。在 shell 脚本中常用于 if、while 等控制结构。方括号 [ ] 是 test 命令的另一种写法。
test EXPRESSION [ EXPRESSION ] # 两种写法等价: test -f /etc/passwd [ -f /etc/passwd ]
注意:[ 和 EXPRESSION 以及 ] 之间必须有空格。
测试文件属性的常用操作符:
-e file - 文件存在
-f file - 文件存在且是普通文件
-d file - 文件存在且是目录
-r file - 文件存在且可读
-w file - 文件存在且可写
-x file - 文件存在且可执行
-s file - 文件存在且大小大于 0
-L file - 文件存在且是符号链接
-O file - 文件存在且属于当前用户
-G file - 文件存在且属于当前组
-N file - 文件存在且上次读取后被修改过
-c file - 文件存在且是字符特殊文件
-b file - 文件存在且是块特殊文件
-S file - 文件存在且是套接字
-p file - 文件存在且是命名管道
文件比较:
file1 -nt file2 - file1 比 file2 新(mtime)
file1 -ot file2 - file1 比 file2 旧
file1 -ef file2 - file1 和 file2 有相同的 device 和 inode
示例:
# 检查文件是否存在
test -f /etc/passwd && echo "文件存在"
# 检查目录是否存在
[ -d /var/log ] && echo "目录存在"
# 检查文件是否可执行
[ -x /usr/bin/munin-cron ] && /usr/bin/munin-cron
# 检查文件是否为空
if [ ! -s /tmp/log.txt ]; then
echo "文件为空或不存在"
fi
# 比较两个文件的新旧
if [ file1.txt -nt file2.txt ]; then
echo "file1.txt 比 file2.txt 新"
fi
字符串比较操作符:
-z string - 字符串长度为 0(空字符串)
-n string - 字符串长度不为 0(非空字符串)
string1 = string2 - 字符串相等
string1 != string2 - 字符串不等
示例:
# 检查变量是否为空
name=""
if [ -z "$name" ]; then
echo "变量为空"
fi
# 检查变量是否非空
name="neo"
[ -n "$name" ] && echo "变量非空: $name"
# 字符串相等比较
user="root"
if [ "$user" = "root" ]; then
echo "是 root 用户"
fi
# 字符串不等比较
if [ "$user" != "guest" ]; then
echo "不是 guest 用户"
fi
# 注意:变量应该用引号包裹,防止 word splitting
[ "$var" = "value" ] # 正确
[ $var = "value" ] # 危险:如果 $var 为空会报错
整数比较操作符:
n1 -eq n2 - n1 等于 n2
n1 -ne n2 - n1 不等于 n2
n1 -lt n2 - n1 小于 n2
n1 -le n2 - n1 小于等于 n2
n1 -gt n2 - n1 大于 n2
n1 -ge n2 - n1 大于等于 n2
示例:
# 检查数字是否相等
count=5
if [ "$count" -eq 5 ]; then
echo "计数为 5"
fi
# 检查数字范围
age=25
if [ "$age" -ge 18 ] && [ "$age" -le 65 ]; then
echo "年龄在 18-65 之间"
fi
# 检查进程数
procs=$(ps aux | wc -l)
if [ "$procs" -gt 100 ]; then
echo "进程数过多: $procs"
fi
# 检查返回值
command
if [ $? -ne 0 ]; then
echo "命令执行失败"
fi
记忆技巧:
eq = equal
ne = not equal
lt = less than
le = less than or equal
gt = greater than
ge = greater than or equal
组合多个条件:
expr1 -a expr2 - 逻辑与(expr1 和 expr2 都为真)
expr1 -o expr2 - 逻辑或(expr1 或 expr2 为真)
! expr - 逻辑非
使用 shell 操作符(推荐):
expr1 && expr2 - 逻辑与
expr1 || expr2 - 逻辑或
! expr - 逻辑非
示例:
# 逻辑与:文件存在且可执行
[ -f /usr/bin/munin-cron ] && [ -x /usr/bin/munin-cron ] && /usr/bin/munin-cron
# 使用 test 的 -a 操作符
test -f /etc/passwd -a -r /etc/passwd && echo "文件存在且可读"
# 逻辑或:目录不存在则创建
[ -d /tmp/test ] || mkdir /tmp/test
# 逻辑非:文件不存在
[ ! -f /tmp/lock ] && echo "锁文件不存在"
# 复杂条件组合
if [ -f "$file" ] && [ -r "$file" ] && [ -s "$file" ]; then
echo "文件存在、可读且非空"
fi
# 嵌套条件
if [ "$user" = "root" ] || ([ "$user" = "admin" ] && [ -w "$file" ]); then
echo "有权限"
fi
# 基本 if 结构
if [ -f /etc/passwd ]; then
echo "文件存在"
fi
# if-else 结构
if [ -d /var/log ]; then
echo "目录存在"
else
echo "目录不存在"
fi
# if-elif-else 结构
if [ -f "$file" ]; then
echo "是普通文件"
elif [ -d "$file" ]; then
echo "是目录"
elif [ -L "$file" ]; then
echo "是符号链接"
else
echo "未知类型"
fi
# 多条件判断
if [ -f "$config" ] && [ -r "$config" ]; then
source "$config"
elif [ -f "$default_config" ]; then
source "$default_config"
else
echo "没有配置文件"
exit 1
fi
# while 循环:条件为真时执行
count=0
while [ $count -lt 5 ]; do
echo "计数: $count"
count=$((count + 1))
done
# until 循环:条件为假时执行
until [ -f /tmp/ready ]; do
echo "等待文件创建..."
sleep 2
done
# 读取用户输入
while [ "$answer" != "yes" ] && [ "$answer" != "no" ]; do
read -p "请输入 yes 或 no: " answer
done
# 检查命令是否存在
if [ -x /usr/bin/git ]; then
echo "git 已安装"
else
echo "git 未安装"
exit 1
fi
# 检查用户权限
if [ "$(id -u)" -ne 0 ]; then
echo "需要 root 权限运行"
exit 1
fi
# 文件备份脚本
src="/data/important.db"
backup="/backup/$(date +%Y%m%d)_important.db"
if [ -f "$src" ] && [ -s "$src" ]; then
cp "$src" "$backup"
echo "备份成功: $backup"
else
echo "源文件不存在或为空"
exit 1
fi
# 检查磁盘空间
available=$(df / | tail -1 | awk '{print $4}')
threshold=1048576 # 1GB in KB
if [ "$available" -lt "$threshold" ]; then
echo "警告:磁盘空间不足 1GB"
fi
# 等待服务启动
timeout=30
count=0
while [ $count -lt $timeout ]; do
if [ -S /var/run/mysql.sock ]; then
echo "MySQL 已启动"
break
fi
sleep 1
count=$((count + 1))
done
if [ $count -eq $timeout ]; then
echo "MySQL 启动超时"
exit 1
fi
Bash 提供 [[ ]] 作为 [ ] 的增强版本:
支持正则表达式匹配:=~
支持 && 和 || 在测试内部使用
不需要对变量加引号(自动处理)
支持模式匹配:== 配合通配符
# 正则表达式匹配
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "有效的邮箱地址"
fi
# 模式匹配
if [[ "$filename" == *.jpg ]]; then
echo "是 JPEG 图片"
fi
# 内部使用逻辑操作符
if [[ -f "$file" && -r "$file" ]]; then
echo "文件存在且可读"
fi
# 字符串比较(不需要引号)
if [[ $user == root ]]; then
echo "是 root 用户"
fi
# 字典序比较
if [[ "apple" < "banana" ]]; then
echo "apple 在 banana 之前"
fi
注意:[[ ]] 是 Bash 扩展,不兼容 POSIX sh。如果需要可移植性,使用 [ ]。
# 错误:方括号内没有空格 if [-f /etc/passwd]; then # 错误! if [ -f /etc/passwd ]; then # 正确 # 错误:整数比较使用字符串操作符 if [ "$count" = "5" ]; then # 字符串比较 if [ "$count" -eq 5 ]; then # 整数比较(正确) # 危险:未加引号的变量 if [ $file = "test.txt" ]; then # 如果 $file 为空会报错 if [ "$file" = "test.txt" ]; then # 安全 # 错误:使用 = 比较数字 if [ $age = 18 ]; then # 字符串比较 if [ $age -eq 18 ]; then # 整数比较(正确) # 错误:在 [ ] 中使用 && 和 || if [ -f file1 && -f file2 ]; then # 错误! if [ -f file1 ] && [ -f file2 ]; then # 正确