- test 命令详解
- 一、test 命令概述
- test 命令选项总结表
- 1.1 基本概念
- 二、文件测试
- 2.1 文件存在性和类型
- 2.2 文件权限测试
- 2.3 文件属性和比较
- 三、字符串测试
- 3.1 基本字符串测试
- 3.2 字符串比较
- 四、数值测试
- 4.1 整数比较
- 4.2 使用算术表达式
- 五、逻辑操作符
- 5.1 基本逻辑操作
- 5.2 使用 -a 和 -o
- 六、test 命令的高级用法
- 6.1 命令替换测试
- 6.2 变量存在性测试
- 七、[[ ]] 与 [ ] 的区别
- 7.1 双方括号的优势
- 7.2 性能比较
- 八、实用示例
- 8.1 文件检查脚本
- 8.2 输入验证脚本
- 8.3 系统检查脚本
- 九、test 命令的替代方案
- 9.1 使用 case 语句
- 9.2 使用 select 菜单
- 十、最佳实践
- 10.1 测试技巧
- 选择建议:
test 命令详解
一、test 命令概述
test 命令选项总结表
| 选项 | 含义 | 示例 |
|---|---|---|
| 文件测试 | ||
-e |
文件存在 | [ -e file ] |
-f |
是普通文件 | [ -f file ] |
-d |
是目录 | [ -d dir ] |
-L |
是符号链接 | [ -L file ] |
-r |
可读 | [ -r file ] |
-w |
可写 | [ -w file ] |
-x |
可执行 | [ -x file ] |
-s |
文件大小大于0 | [ -s file ] |
-O |
当前用户是所有者 | [ -O file ] |
-G |
当前用户在属组 | [ -G file ] |
-nt |
文件1比文件2新 | [ f1 -nt f2 ] |
-ot |
文件1比文件2旧 | [ f1 -ot f2 ] |
-ef |
是同一文件 | [ f1 -ef f2 ] |
| 字符串测试 | ||
-z |
字符串为空 | [ -z "$str" ] |
-n |
字符串非空 | [ -n "$str" ] |
= |
字符串相等 | [ "$s1" = "$s2" ] |
!= |
字符串不等 | [ "$s1" != "$s2" ] |
< |
按字典序小于 | [[ "$s1" < "$s2" ]] |
> |
按字典序大于 | [[ "$s1" > "$s2" ]] |
| 数值测试 | ||
-eq |
等于 | [ n1 -eq n2 ] |
-ne |
不等于 | [ n1 -ne n2 ] |
-lt |
小于 | [ n1 -lt n2 ] |
-le |
小于等于 | [ n1 -le n2 ] |
-gt |
大于 | [ n1 -gt n2 ] |
-ge |
大于等于 | [ n1 -ge n2 ] |
| 逻辑操作 | ||
! |
非 | [ ! -f file ] |
-a |
与 | [ -f f1 -a -f f2 ] |
-o |
或 | [ -f f1 -o -f f2 ] |
1.1 基本概念
test 是 Shell 中用于条件测试的命令,也称为 [ 命令。
# 两种语法等价
test expression
[ expression ]
# 注意:方括号内部必须有空格
[ -f file.txt ] # 正确
[-f file.txt] # 错误二、文件测试
2.1 文件存在性和类型
#!/bin/bash
# 文件存在性测试
file="test.txt"
# 检查文件是否存在
if [ -e "$file" ]; then
echo "$file 存在"
fi
# 检查是否是普通文件
if [ -f "$file" ]; then
echo "$file 是普通文件"
fi
# 检查是否是目录
if [ -d "$file" ]; then
echo "$file 是目录"
fi
# 检查是否是符号链接
if [ -L "$file" ]; then
echo "$file 是符号链接"
fi
# 检查是否是块设备文件
if [ -b "$file" ]; then
echo "$file 是块设备文件"
fi
# 检查是否是字符设备文件
if [ -c "$file" ]; then
echo "$file 是字符设备文件"
fi
# 检查是否是命名管道
if [ -p "$file" ]; then
echo "$file 是命名管道"
fi
# 检查是否是套接字文件
if [ -S "$file" ]; then
echo "$file 是套接字文件"
fi2.2 文件权限测试
#!/bin/bash
# 文件权限测试
file="script.sh"
# 检查文件是否可读
if [ -r "$file" ]; then
echo "$file 可读"
fi
# 检查文件是否可写
if [ -w "$file" ]; then
echo "$file 可写"
fi
# 检查文件是否可执行
if [ -x "$file" ]; then
echo "$file 可执行"
fi
# 检查文件是否设置了 SUID 位
if [ -u "$file" ]; then
echo "$file 设置了 SUID 位"
fi
# 检查文件是否设置了 SGID 位
if [ -g "$file" ]; then
echo "$file 设置了 SGID 位"
fi
# 检查文件是否设置了粘滞位
if [ -k "$file" ]; then
echo "$file 设置了粘滞位"
fi2.3 文件属性和比较
#!/bin/bash
# 文件属性和比较测试
file1="file1.txt"
file2="file2.txt"
# 检查文件是否非空
if [ -s "$file1" ]; then
echo "$file1 非空"
fi
# 检查文件是否为空
if [ ! -s "$file1" ]; then
echo "$file1 为空"
fi
# 检查文件描述符是否在终端打开
if [ -t 0 ]; then
echo "标准输入来自终端"
fi
# 检查文件是否设置了 set-group-id
if [ -g "$file1" ]; then
echo "$file1 设置了 set-group-id"
fi
# 比较两个文件
if [ "$file1" -nt "$file2" ]; then
echo "$file1 比 $file2 新"
fi
if [ "$file1" -ot "$file2" ]; then
echo "$file1 比 $file2 旧"
fi
if [ "$file1" -ef "$file2" ]; then
echo "$file1 和 $file2 是同一个文件(硬链接)"
fi三、字符串测试
3.1 基本字符串测试
#!/bin/bash
# 字符串测试
str1="hello"
str2="world"
empty=""
# 检查字符串是否为空
if [ -z "$empty" ]; then
echo "字符串为空"
fi
# 检查字符串是否非空
if [ -n "$str1" ]; then
echo "字符串非空: $str1"
fi
# 字符串相等测试
if [ "$str1" = "hello" ]; then
echo "字符串等于 hello"
fi
# 字符串不相等测试
if [ "$str1" != "$str2" ]; then
echo "$str1 不等于 $str2"
fi
# 使用 == 进行模式匹配(需要双方括号)
if [[ "$str1" == h* ]]; then
echo "$str1 匹配模式 h*"
fi3.2 字符串比较
#!/bin/bash
# 字符串比较
str1="apple"
str2="banana"
str3="Apple"
# 检查字符串是否相等(区分大小写)
if [ "$str1" = "apple" ]; then
echo "$str1 等于 apple"
fi
# 使用双方括号进行模式匹配
if [[ "$str1" == a* ]]; then
echo "$str1 以 a 开头"
fi
if [[ "$str1" == *e ]]; then
echo "$str1 以 e 结尾"
fi
if [[ "$str1" == *pp* ]]; then
echo "$str1 包含 pp"
fi
# 正则表达式匹配
if [[ "$str1" =~ ^a ]]; then
echo "$str1 以 a 开头(正则)"
fi
if [[ "$str1" =~ p{2} ]]; then
echo "$str1 包含两个连续的 p"
fi
# 字典序比较
if [[ "$str1" < "$str2" ]]; then
echo "$str1 在字典序上小于 $str2"
fi四、数值测试
4.1 整数比较
#!/bin/bash
# 数值测试
num1=10
num2=20
num3=10
# 相等比较
if [ $num1 -eq $num3 ]; then
echo "$num1 等于 $num3"
fi
# 不相等比较
if [ $num1 -ne $num2 ]; then
echo "$num1 不等于 $num2"
fi
# 大于比较
if [ $num2 -gt $num1 ]; then
echo "$num2 大于 $num1"
fi
# 大于或等于
if [ $num2 -ge $num1 ]; then
echo "$num2 大于或等于 $num1"
fi
# 小于比较
if [ $num1 -lt $num2 ]; then
echo "$num1 小于 $num2"
fi
# 小于或等于
if [ $num1 -le $num2 ]; then
echo "$num1 小于或等于 $num2"
fi4.2 使用算术表达式
#!/bin/bash
# 使用双括号进行算术比较
num1=10
num2=20
# 使用 (()) 进行算术运算
if (( num1 < num2 )); then
echo "$num1 < $num2"
fi
if (( num1 == 10 )); then
echo "$num1 等于 10"
fi
if (( num2 != 10 )); then
echo "$num2 不等于 10"
fi
if (( num2 >= 20 )); then
echo "$num2 大于等于 20"
fi
# 复合条件
if (( num1 > 5 && num2 < 30 )); then
echo "$num1 > 5 且 $num2 < 30"
fi
if (( num1 < 5 || num2 > 15 )); then
echo "$num1 < 5 或 $num2 > 15"
fi五、逻辑操作符
5.1 基本逻辑操作
#!/bin/bash
# 逻辑操作符
file="test.txt"
num=10
# NOT 操作
if [ ! -f "$file" ]; then
echo "$file 不是普通文件"
fi
# AND 操作
if [ -f "$file" ] && [ -r "$file" ]; then
echo "$file 是普通文件且可读"
fi
# OR 操作
if [ ! -f "$file" ] || [ ! -r "$file" ]; then
echo "$file 不是普通文件或不可读"
fi
# 组合条件
if [ $num -gt 5 ] && [ $num -lt 20 ]; then
echo "$num 在 5 和 20 之间"
fi
if [ $num -lt 5 ] || [ $num -gt 20 ]; then
echo "$num 小于 5 或大于 20"
fi5.2 使用 -a 和 -o
#!/bin/bash
# 使用 -a 和 -o(不推荐,已弃用)
file="test.txt"
num=15
# -a 表示 AND
if [ -f "$file" -a -r "$file" ]; then
echo "$file 存在且可读"
fi
# -o 表示 OR
if [ ! -f "$file" -o ! -r "$file" ]; then
echo "$file 不存在或不可读"
fi
# 复合条件
if [ $num -gt 10 -a $num -lt 20 ]; then
echo "$num 在 10 和 20 之间"
fi六、test 命令的高级用法
6.1 命令替换测试
#!/bin/bash
# 测试命令输出
# 测试命令是否成功执行
if grep -q "error" logfile.txt; then
echo "日志文件中找到 error"
fi
# 测试命令输出是否为空
output=$(ls /tmp)
if [ -n "$output" ]; then
echo "/tmp 目录不为空"
fi
# 测试命令返回值
if wc -l < data.txt > /dev/null 2>&1; then
echo "data.txt 存在且可读"
else
echo "无法读取 data.txt"
fi6.2 变量存在性测试
#!/bin/bash
# 变量存在性测试
# 测试变量是否设置
if [ -v VAR_NAME ]; then
echo "VAR_NAME 已设置"
fi
# 测试变量是否未设置
if [ ! -v UNDEFINED_VAR ]; then
echo "UNDEFINED_VAR 未设置"
fi
# 测试变量是否已设置且非空
my_var=""
if [ -v my_var ]; then
echo "my_var 已设置(即使是空值)"
fi
if [ -n "${my_var+x}" ]; then
echo "my_var 已声明(包括空值)"
fi
if [ -n "${my_var}" ]; then
echo "my_var 已设置且非空"
else
echo "my_var 未设置或为空"
fi七、[[ ]] 与 [ ] 的区别
7.1 双方括号的优势
#!/bin/bash
# [[ ]] 与 [ ] 对比
str="hello world"
pattern="hello*"
num=10
# 1. 模式匹配(只有 [[ ]] 支持)
if [[ "$str" == hello* ]]; then
echo "模式匹配成功"
fi
# 2. 正则表达式匹配
if [[ "$str" =~ ^h ]]; then
echo "正则匹配成功"
fi
# 3. 无需引号
if [[ $str == hello* ]]; then
echo "无需引号也能工作"
fi
# 4. 逻辑操作符
if [[ $num -gt 5 && $num -lt 20 ]]; then
echo "使用 && 操作符"
fi
# 5. 更安全的空变量处理
unset unset_var
if [[ $unset_var == "" ]]; then
echo "空变量不会报错"
fi7.2 性能比较
#!/bin/bash
# 性能测试
count=10000
# 测试 [ ] 性能
time for ((i=0; i<count; i++)); do
[ 1 -eq 1 ] >/dev/null
done
# 测试 [[ ]] 性能
time for ((i=0; i<count; i++)); do
[[ 1 == 1 ]] >/dev/null
done
# 测试 (( )) 性能
time for ((i=0; i<count; i++)); do
(( 1 == 1 )) >/dev/null
done八、实用示例
8.1 文件检查脚本
#!/bin/bash
# 文件检查脚本
check_file() {
local file="$1"
echo "检查文件: $file"
echo "========================="
# 存在性检查
if [ ! -e "$file" ]; then
echo "❌ 文件不存在"
return 1
fi
echo "✓ 文件存在"
# 类型检查
if [ -f "$file" ]; then
echo "✓ 是普通文件"
elif [ -d "$file" ]; then
echo "✓ 是目录"
elif [ -L "$file" ]; then
echo "✓ 是符号链接"
else
echo "⚠ 未知文件类型"
fi
# 权限检查
local perms=""
[ -r "$file" ] && perms+="r" || perms+="-"
[ -w "$file" ] && perms+="w" || perms+="-"
[ -x "$file" ] && perms+="x" || perms+="-"
echo "权限: $perms"
# 大小检查
if [ -s "$file" ]; then
size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null)
echo "大小: $size 字节"
else
echo "大小: 0 字节(空文件)"
fi
# 时间检查
if [ -f "$file" ]; then
modified=$(stat -c%Y "$file" 2>/dev/null || stat -f%m "$file" 2>/dev/null)
now=$(date +%s)
age=$(( (now - modified) / 86400 ))
echo "最后修改: $age 天前"
fi
echo ""
return 0
}
# 检查多个文件
for file in "$@"; do
check_file "$file"
done8.2 输入验证脚本
#!/bin/bash
# 输入验证脚本
validate_input() {
local input="$1"
local min_len="${2:-1}"
local max_len="${3:-100}"
# 检查是否为空
if [ -z "$input" ]; then
echo "错误: 输入不能为空"
return 1
fi
# 检查长度
local len=${#input}
if [ $len -lt $min_len ]; then
echo "错误: 输入太短(最少 $min_len 个字符)"
return 1
fi
if [ $len -gt $max_len ]; then
echo "错误: 输入太长(最多 $max_len 个字符)"
return 1
fi
# 检查是否只包含字母数字
if [[ ! "$input" =~ ^[a-zA-Z0-9_]+$ ]]; then
echo "错误: 只能包含字母、数字和下划线"
return 1
fi
echo "✓ 输入有效"
return 0
}
validate_number() {
local input="$1"
local min="${2:--2147483648}"
local max="${3:-2147483647}"
# 检查是否为数字
if [[ ! "$input" =~ ^-?[0-9]+$ ]]; then
echo "错误: 必须为整数"
return 1
fi
# 检查范围
if (( input < min )); then
echo "错误: 不能小于 $min"
return 1
fi
if (( input > max )); then
echo "错误: 不能大于 $max"
return 1
fi
echo "✓ 数字有效"
return 0
}
validate_email() {
local email="$1"
# 简单电子邮件验证
if [[ ! "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "错误: 电子邮件格式无效"
return 1
fi
echo "✓ 电子邮件有效"
return 0
}
# 使用示例
read -p "请输入用户名: " username
validate_input "$username" 3 20
read -p "请输入年龄: " age
validate_number "$age" 0 150
read -p "请输入电子邮件: " email
validate_email "$email"8.3 系统检查脚本
#!/bin/bash
# 系统检查脚本
check_system() {
echo "=== 系统检查报告 ==="
echo "检查时间: $(date)"
echo
# 检查磁盘空间
echo "1. 磁盘空间检查:"
df -h | grep -E '^(Filesystem|/dev/)' | while read line; do
usage=$(echo "$line" | awk '{print $5}' | sed 's/%//')
if [ "$usage" -gt 80 ]; then
echo "⚠ 警告: $line"
elif [ "$usage" -gt 90 ]; then
echo "❌ 严重: $line"
else
echo "✓ 正常: $line"
fi
done
echo
# 检查内存使用
echo "2. 内存使用检查:"
free -h | head -2
echo
# 检查负载
echo "3. 系统负载检查:"
load=$(uptime | awk -F'load average:' '{print $2}')
echo "负载: $load"
echo
# 检查服务状态
echo "4. 服务状态检查:"
services=("sshd" "nginx" "mysql" "docker")
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service"; then
echo "✓ $service: 运行中"
else
echo "❌ $service: 停止"
fi
done
echo
# 检查端口监听
echo "5. 网络端口检查:"
ports=("22" "80" "443" "3306")
for port in "${ports[@]}"; do
if ss -tuln | grep -q ":$port "; then
echo "✓ 端口 $port: 监听中"
else
echo "❌ 端口 $port: 未监听"
fi
done
echo
# 检查文件系统
echo "6. 重要文件检查:"
important_files=("/etc/passwd" "/etc/shadow" "/etc/ssh/sshd_config")
for file in "${important_files[@]}"; do
if [ -f "$file" ]; then
perms=$(stat -c "%a" "$file")
if [ "$perms" != "600" ] && [ "$perms" != "644" ]; then
echo "⚠ $file: 权限 $perms 可能不安全"
else
echo "✓ $file: 权限 $perms 正常"
fi
else
echo "❌ $file: 文件不存在"
fi
done
}
# 运行检查
check_system九、test 命令的替代方案
9.1 使用 case 语句
#!/bin/bash
# 使用 case 进行模式匹配
read -p "请输入命令: " cmd
case "$cmd" in
start|begin)
echo "开始执行"
;;
stop|end)
echo "停止执行"
;;
restart|reload)
echo "重新启动"
;;
status|info)
echo "查看状态"
;;
*)
echo "未知命令: $cmd"
;;
esac9.2 使用 select 菜单
#!/bin/bash
# 使用 select 创建菜单
PS3="请选择操作 (1-5): "
options=("查看状态" "启动服务" "停止服务" "重启服务" "退出")
select opt in "${options[@]}"
do
case $opt in
"查看状态")
echo "正在查看状态..."
;;
"启动服务")
echo "正在启动服务..."
;;
"停止服务")
echo "正在停止服务..."
;;
"重启服务")
echo "正在重启服务..."
;;
"退出")
break
;;
*)
echo "无效选项 $REPLY"
;;
esac
done十、最佳实践
10.1 测试技巧
#!/bin/bash
# 最佳实践示例
# 1. 总是引用变量
file="my file.txt"
if [ -f "$file" ]; then
echo "文件存在"
fi
# 2. 使用 [[ ]] 替代 [ ]
if [[ -f "$file" && -r "$file" ]]; then
echo "文件存在且可读"
fi
# 3. 使用 (( )) 进行数值比较
num=10
if (( num > 5 && num < 20 )); then
echo "数字在范围内"
fi
# 4. 检查命令是否存在
if command -v docker &> /dev/null; then
echo "Docker 已安装"
else
echo "Docker 未安装"
fi
# 5. 检查文件描述符
if [ -t 0 ]; then
echo "交互模式"
else
echo "非交互模式"
fi
# 6. 使用临时文件
tmpfile=$(mktemp)
if [ -f "$tmpfile" ]; then
echo "临时文件创建成功: $tmpfile"
# 使用临时文件
rm "$tmpfile"
fi选择建议:
- 使用
[[ ]]而不是[ ](更安全,功能更多) - 使用
(( ))进行数值比较 - 总是引用变量
- 使用
-z和-n检查字符串 - 使用
-v检查变量是否设置 - 优先使用模式匹配而不是复杂正则
作者:严锋 创建时间:2025-12-20 12:35
最后编辑:严锋 更新时间:2025-12-25 10:39
最后编辑:严锋 更新时间:2025-12-25 10:39