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 是套接字文件"
fi

2.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 设置了粘滞位"
fi

2.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*"
fi

3.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"
fi

4.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"
fi

5.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"
fi

6.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 "空变量不会报错"
fi

7.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"
done

8.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"
        ;;
esac

9.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

选择建议:

  1. 使用 [[ ]] 而不是 [ ](更安全,功能更多)
  2. 使用 (( )) 进行数值比较
  3. 总是引用变量
  4. 使用 -z-n 检查字符串
  5. 使用 -v 检查变量是否设置
  6. 优先使用模式匹配而不是复杂正则
作者:严锋  创建时间:2025-12-20 12:35
最后编辑:严锋  更新时间:2025-12-25 10:39