文件测试运算符 #
一、文件类型测试 #
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