文件测试运算符 #

一、文件类型测试 #

1.1 基本文件类型运算符 #

运算符 说明 示例
-e 文件存在 [ -e file ]
-f 普通文件 [ -f file ]
-d 目录 [ -d dir ]
-L 符号链接 [ -L link ]
-b 块设备 [ -b file ]
-c 字符设备 [ -c file ]
-p 命名管道 [ -p file ]
-S Socket文件 [ -S file ]

1.2 文件存在测试 #

bash
#!/bin/bash

file="/etc/passwd"

# 测试文件是否存在
if [ -e "$file" ]; then
    echo "文件 $file 存在"
fi

# 测试是否为普通文件
if [ -f "$file" ]; then
    echo "$file 是普通文件"
fi

# 测试是否为目录
dir="/etc"
if [ -d "$dir" ]; then
    echo "$dir 是目录"
fi

# 测试符号链接
link="/bin/sh"
if [ -L "$link" ]; then
    echo "$link 是符号链接"
fi

1.3 特殊文件类型测试 #

bash
#!/bin/bash

# 块设备
if [ -b "/dev/sda" ]; then
    echo "/dev/sda 是块设备"
fi

# 字符设备
if [ -c "/dev/tty" ]; then
    echo "/dev/tty 是字符设备"
fi

# Socket文件
if [ -S "/var/run/docker.sock" ]; then
    echo "Docker socket 存在"
fi

# 命名管道
if [ -p "/tmp/mypipe" ]; then
    echo "/tmp/mypipe 是命名管道"
fi

二、文件权限测试 #

2.1 权限测试运算符 #

运算符 说明 示例
-r 可读 [ -r file ]
-w 可写 [ -w file ]
-x 可执行 [ -x file ]
-u SUID位设置 [ -u file ]
-g SGID位设置 [ -g file ]
-k Sticky位设置 [ -k file ]

2.2 基本权限测试 #

bash
#!/bin/bash

file="/etc/passwd"

# 测试可读
if [ -r "$file" ]; then
    echo "$file 可读"
fi

# 测试可写
if [ -w "$file" ]; then
    echo "$file 可写"
fi

# 测试可执行
script="test.sh"
if [ -x "$script" ]; then
    echo "$script 可执行"
fi

# 组合测试
if [ -r "$file" ] && [ -w "$file" ]; then
    echo "$file 可读可写"
fi

2.3 特殊权限测试 #

bash
#!/bin/bash

# 测试SUID
if [ -u "/usr/bin/passwd" ]; then
    echo "passwd 有SUID位"
fi

# 测试SGID
if [ -g "/usr/bin/wall" ]; then
    echo "wall 有SGID位"
fi

# 测试Sticky位
if [ -k "/tmp" ]; then
    echo "/tmp 有Sticky位"
fi

三、文件属性测试 #

3.1 属性测试运算符 #

运算符 说明 示例
-s 文件非空(大小>0) [ -s file ]
-N 文件被修改过 [ -N file ]
file1 -nt file2 file1比file2新 [ file1 -nt file2 ]
file1 -ot file2 file1比file2旧 [ file1 -ot file2 ]
file1 -ef file2 相同文件 [ file1 -ef file2 ]

3.2 文件大小测试 #

bash
#!/bin/bash

file="data.txt"

# 测试文件是否非空
if [ -s "$file" ]; then
    echo "$file 不为空"
else
    echo "$file 为空或不存在"
fi

# 获取文件大小
if [ -f "$file" ]; then
    size=$(stat -c %s "$file")
    echo "文件大小: $size 字节"
fi

3.3 文件比较 #

bash
#!/bin/bash

file1="source.txt"
file2="target.txt"

# 比较修改时间
if [ "$file1" -nt "$file2" ]; then
    echo "$file1 比 $file2 新"
elif [ "$file1" -ot "$file2" ]; then
    echo "$file1 比 $file2 旧"
else
    echo "文件修改时间相同"
fi

# 测试是否为同一文件(硬链接)
if [ "$file1" -ef "$file2" ]; then
    echo "两个文件是同一个文件"
fi

# 测试文件是否被修改
if [ -N "$file1" ]; then
    echo "$file1 自上次读取后被修改过"
fi

四、文件测试实战 #

4.1 文件检查函数 #

bash
#!/bin/bash

check_file() {
    local file="$1"
    
    # 检查文件是否存在
    if [ ! -e "$file" ]; then
        echo "错误: 文件 $file 不存在"
        return 1
    fi
    
    # 显示文件信息
    echo "=== 文件信息 ==="
    echo "路径: $file"
    
    if [ -f "$file" ]; then
        echo "类型: 普通文件"
        echo "大小: $(stat -c %s "$file") 字节"
    elif [ -d "$file" ]; then
        echo "类型: 目录"
        echo "文件数: $(ls -1 "$file" | wc -l)"
    elif [ -L "$file" ]; then
        echo "类型: 符号链接"
        echo "目标: $(readlink "$file")"
    fi
    
    echo "权限: $(stat -c %a "$file")"
    echo "所有者: $(stat -c %U "$file")"
    echo "修改时间: $(stat -c %y "$file")"
    
    # 权限检查
    [ -r "$file" ] && echo "可读: 是" || echo "可读: 否"
    [ -w "$file" ] && echo "可写: 是" || echo "可写: 否"
    [ -x "$file" ] && echo "可执行: 是" || echo "可执行: 否"
}

check_file "/etc/passwd"

4.2 目录遍历检查 #

bash
#!/bin/bash

traverse_dir() {
    local dir="$1"
    
    # 检查目录是否存在
    if [ ! -d "$dir" ]; then
        echo "错误: $dir 不是目录"
        return 1
    fi
    
    # 检查是否可读
    if [ ! -r "$dir" ]; then
        echo "错误: 目录 $dir 不可读"
        return 1
    fi
    
    # 遍历目录
    for item in "$dir"/*; do
        if [ -f "$item" ]; then
            echo "文件: $item"
        elif [ -d "$item" ]; then
            echo "目录: $item"
        elif [ -L "$item" ]; then
            echo "链接: $item"
        fi
    done
}

traverse_dir "/etc"

4.3 备份检查脚本 #

bash
#!/bin/bash

backup_check() {
    local source="$1"
    local backup="$2"
    
    # 检查源文件
    if [ ! -e "$source" ]; then
        echo "错误: 源文件 $source 不存在"
        return 1
    fi
    
    # 检查备份文件
    if [ ! -e "$backup" ]; then
        echo "备份文件不存在,需要备份"
        return 1
    fi
    
    # 比较文件
    if [ "$source" -nt "$backup" ]; then
        echo "源文件更新,需要重新备份"
        return 1
    fi
    
    echo "备份是最新的"
    return 0
}

backup_check "source.txt" "backup/source.txt"

4.4 临时文件处理 #

bash
#!/bin/bash

# 创建临时文件
create_temp_file() {
    local prefix="${1:-temp}"
    local temp_file=$(mktemp "/tmp/${prefix}.XXXXXX")
    
    # 设置退出时清理
    trap "rm -f '$temp_file'" EXIT
    
    echo "$temp_file"
}

# 使用临时文件
temp=$(create_temp_file "myapp")
echo "临时文件: $temp"
echo "测试数据" > "$temp"

# 检查临时文件
if [ -s "$temp" ]; then
    echo "临时文件有内容"
fi

# 脚本结束自动清理

五、文件测试最佳实践 #

5.1 安全检查模式 #

bash
#!/bin/bash

safe_read_file() {
    local file="$1"
    
    # 完整检查
    [[ -z "$file" ]] && { echo "错误: 文件名为空"; return 1; }
    [[ ! -e "$file" ]] && { echo "错误: 文件 $file 不存在"; return 1; }
    [[ ! -f "$file" ]] && { echo "错误: $file 不是普通文件"; return 1; }
    [[ ! -r "$file" ]] && { echo "错误: 文件 $file 不可读"; return 1; }
    
    # 安全读取
    cat "$file"
}

5.2 目录操作模式 #

bash
#!/bin/bash

ensure_dir() {
    local dir="$1"
    
    # 检查是否存在
    if [ -e "$dir" ]; then
        # 存在但不是目录
        if [ ! -d "$dir" ]; then
            echo "错误: $dir 存在但不是目录"
            return 1
        fi
    else
        # 创建目录
        mkdir -p "$dir" || { echo "错误: 无法创建目录 $dir"; return 1; }
    fi
    
    # 检查权限
    [ -w "$dir" ] || { echo "错误: 目录 $dir 不可写"; return 1; }
    
    return 0
}

ensure_dir "/tmp/myapp/data"

5.3 文件锁机制 #

bash
#!/bin/bash

lock_file="/tmp/myapp.lock"

acquire_lock() {
    # 检查锁文件是否存在
    if [ -e "$lock_file" ]; then
        # 检查进程是否还在运行
        local pid=$(cat "$lock_file" 2>/dev/null)
        if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
            echo "另一个实例正在运行 (PID: $pid)"
            return 1
        fi
        # 清理过期的锁文件
        rm -f "$lock_file"
    fi
    
    # 创建锁文件
    echo $$ > "$lock_file"
    return 0
}

release_lock() {
    rm -f "$lock_file"
}

# 使用锁
if acquire_lock; then
    trap release_lock EXIT
    echo "执行任务..."
    # 你的代码
fi

六、文件测试运算符速查表 #

6.1 类型测试 #

运算符 说明
-e 存在
-f 普通文件
-d 目录
-L 符号链接
-b 块设备
-c 字符设备
-p 命名管道
-S Socket

6.2 权限测试 #

运算符 说明
-r 可读
-w 可写
-x 可执行
-u SUID
-g SGID
-k Sticky

6.3 属性测试 #

运算符 说明
-s 非空
-N 已修改
-nt 更新
-ot 更旧
-ef 相同

七、总结 #

7.1 关键要点 #

  • 使用 -e 测试文件是否存在
  • 使用 -f-d 区分文件和目录
  • 使用 -r-w-x 测试权限
  • 使用 -nt-ot 比较文件时间
  • 组合使用进行完整检查

7.2 下一步 #

你已经掌握了文件测试运算符,接下来让我们学习 条件判断if,了解Shell中的条件控制!

最后更新:2026-03-27