test [ ] [[ ]] 判断 #
1. 核心概念:一切皆命令 #
在 Shell 脚本中,if 后面跟的是一个命令,而不是一个"条件表达式"。命令执行后返回退出码(exit code):
- 0:表示成功(条件为真)
- 非 0:表示失败(条件为假)
# if 实际上是在检查命令的退出码
if command; then
echo "命令执行成功(退出码为 0)"
fi
验证这一点:
# 直接运行 test 命令,看不到输出
test -f "123.MP4"
# 用 $? 查看退出码
echo $? # 0 表示文件存在,1 表示不存在
2. test 命令详解 #
test 是一个内置命令,专门用于文件检查、字符串比较和数值比较。
基本语法 #
test 条件
示例 #
# 检查文件是否存在
test -f "123.MP4"
echo $? # 0=存在,1=不存在
# 检查数字是否相等
test 5 -eq 5
echo $? # 0(真)
# 检查字符串是否为空
test -z ""
echo $? # 0(空字符串为真)
在 if 中使用 #
if test -f "123.MP4"; then
echo "文件存在"
else
echo "文件不存在"
fi
3. 单括号 [ ] 详解
#
[ ] 是 test 命令的别名(语法糖),两者完全等价。
基本语法 #
[ 条件 ]
⚠️ 关键规则:
[和]前后必须有空格- 条件和操作符之间必须有空格
正确写法 #
# ✅ 正确:有空格
if [ -f "123.MP4" ]; then
echo "文件存在"
fi
错误写法 #
# ❌ 错误:缺少空格
if [-f "123.MP4"]; then
echo "这行会报错"
fi
# ❌ 错误:操作符和文件名之间缺少空格
if [ -f"123.MP4" ]; then
echo "这行也会报错"
fi
为什么 [ ] 需要空格?
#
因为 [ 本身是一个命令(/usr/bin/[),它需要接收参数。Shell 用空格来分隔命令和参数:
# [ 是一个命令,-f 是参数1,"123.MP4" 是参数2,] 是参数3
[ -f "123.MP4" ]
4. 双括号 [[ ]] 详解
#
[[ ]] 是 Bash/Zsh 等高级 Shell 的增强版条件判断,功能更强大、更安全。
基本语法 #
[[ 条件 ]]
优势对比 #
| 特性 | [ ] |
[[ ]] |
|---|---|---|
| POSIX 兼容 | ✅ 是 | ❌ 否(仅 Bash/Zsh) |
| 变量不加引号 | ❌ 必须加 | ✅ 可以不加(但建议加) |
| 支持正则表达式 | ❌ 不支持 | ✅ 支持 =~ |
支持 && ` |
` | |
| 支持通配符匹配 | ❌ 不支持 | ✅ 支持 == 通配符 |
| 字符串比较 | 容易出错 | 更安全 |
示例 #
# 1. 变量不加引号也安全(但建议加)
file="123.MP4"
if [[ -f $file ]]; then # 可以不加引号
echo "文件存在"
fi
# 2. 支持正则匹配(文件名以数字开头)
if [[ "123.MP4" =~ ^[0-9] ]]; then
echo "文件名以数字开头"
fi
# 3. 支持通配符
if [[ "123.MP4" == *.MP4 ]]; then
echo "MP4 文件"
fi
# 4. 支持逻辑运算(&& 和 ||)
if [[ -f "123.MP4" && -r "123.MP4" ]]; then
echo "文件存在且可读"
fi
5. 三种写法对比总结 #
| 写法 | 等价关系 | 适用场景 | 推荐度 |
|---|---|---|---|
test -f file |
[ -f file ] |
POSIX 兼容脚本(sh) | ⭐⭐⭐ |
[ -f file ] |
test -f file |
POSIX 兼容脚本(sh) | ⭐⭐⭐ |
[[ -f file ]] |
增强版,功能更强 | Bash 脚本(#!/bin/bash) | ⭐⭐⭐⭐⭐ |
完整等价示例 #
# 这三种写法完全等价
if test -f "123.MP4"; then echo "存在"; fi
if [ -f "123.MP4" ]; then echo "存在"; fi
if [[ -f "123.MP4" ]]; then echo "存在"; fi
6. 常用判断条件大全 #
文件判断 #
| 条件 | 含义 |
|---|---|
-f file |
文件存在且是普通文件 |
-d dir |
目录存在 |
-e file |
文件存在(任何类型) |
-r file |
文件存在且可读 |
-w file |
文件存在且可写 |
-x file |
文件存在且可执行 |
-s file |
文件存在且大小不为 0 |
-L file |
文件存在且是符号链接 |
示例:
if [ -f "123.MP4" ]; then
echo "是普通文件"
fi
if [ -d "videos" ]; then
echo "是目录"
fi
if [ -e "123.MP4" ]; then
echo "文件存在(不管是文件还是目录)"
fi
字符串判断 #
| 条件 | 含义 |
|---|---|
-z str |
字符串为空 |
-n str |
字符串非空 |
str1 = str2 |
字符串相等 |
str1 != str2 |
字符串不等 |
示例:
name=""
if [ -z "$name" ]; then
echo "名字为空"
fi
if [ "$name" = "admin" ]; then
echo "欢迎管理员"
fi
数值判断 #
| 条件 | 含义 |
|---|---|
num1 -eq num2 |
等于 |
num1 -ne num2 |
不等于 |
num1 -gt num2 |
大于 |
num1 -ge num2 |
大于等于 |
num1 -lt num2 |
小于 |
num1 -le num2 |
小于等于 |
示例:
count=10
if [ $count -gt 5 ]; then
echo "count 大于 5"
fi
逻辑运算 #
[ ] 写法 |
[[ ]] 写法 |
含义 |
|---|---|---|
[ -f file -a -r file ] |
[[ -f file && -r file ]] |
与(AND) |
[ -f file -o -d file ] |
[[ -f file || -d file ]] |
或(OR) |
[ ! -f file ] |
[[ ! -f file ]] |
非(NOT) |
推荐:在 [[ ]] 中使用 &&、||、! 更直观。
7. 实战示例 #
示例 1:检查并安装软件(你之前的场景) #
#!/bin/bash
# 检查 ffmpeg 是否存在,不存在则安装
if ! command -v ffmpeg >/dev/null 2>&1; then
echo "ffmpeg 未安装,开始安装..."
apt update
apt install -y ffmpeg
fi
# 检查视频文件是否存在
if [ -f "123.MP4" ]; then
echo "找到 123.MP4,开始转换..."
ffmpeg -i "123.MP4" output.mp4
else
echo "错误:123.MP4 不存在"
exit 1
fi
示例 2:备份脚本 #
#!/bin/bash
backup_dir="/backup"
source_file="/etc/nginx/nginx.conf"
# 检查源文件是否存在且可读
if [[ -f "$source_file" && -r "$source_file" ]]; then
echo "源文件存在且可读"
else
echo "错误:源文件不存在或不可读"
exit 1
fi
# 检查备份目录是否存在,不存在则创建
if [ ! -d "$backup_dir" ]; then
echo "备份目录不存在,正在创建..."
mkdir -p "$backup_dir"
fi
# 执行备份
cp "$source_file" "$backup_dir/nginx.conf.bak"
echo "备份完成"
示例 3:用户输入判断 #
#!/bin/bash
read -p "请输入你的年龄: " age
# 判断输入是否为数字(使用正则)
if [[ ! "$age" =~ ^[0-9]+$ ]]; then
echo "错误:请输入数字"
exit 1
fi
# 判断年龄范围
if [ "$age" -ge 18 ] && [ "$age" -le 60 ]; then
echo "年龄符合要求(18-60 岁)"
else
echo "年龄不在范围内"
fi
8. 常见错误避坑 #
错误 1:缺少空格 #
# ❌ 错误
if [-f "file"]; then
if [ -f"file" ]; then
# ✅ 正确
if [ -f "file" ]; then
错误 2:变量不加引号(导致字符串为空时语法错误) #
file="" # 文件名为空
# ❌ 错误:展开后变成 if [ -f ],语法错误
if [ -f $file ]; then
echo "存在"
fi
# ✅ 正确:加引号保护
if [ -f "$file" ]; then
echo "存在"
fi
错误 3:[ ] 中使用 && 或 ||
#
# ❌ 错误:`[ ]` 不支持 `&&`
if [ -f file1 && -f file2 ]; then
echo "两个文件都存在"
fi
# ✅ 正确方式 1:使用 `-a`
if [ -f file1 -a -f file2 ]; then
echo "两个文件都存在"
fi
# ✅ 正确方式 2:使用 `[[ ]]`
if [[ -f file1 && -f file2 ]]; then
echo "两个文件都存在"
fi
# ✅ 正确方式 3:分开写
if [ -f file1 ] && [ -f file2 ]; then
echo "两个文件都存在"
fi
错误 4:> 和 < 在 [ ] 中被解释为重定向
#
# ❌ 错误:Shell 会尝试重定向
if [ 5 > 3 ]; then
echo "5 大于 3"
fi
# ✅ 正确:使用 -gt
if [ 5 -gt 3 ]; then
echo "5 大于 3"
fi
# ✅ 或者使用 (( )) 进行算术比较
if (( 5 > 3 )); then
echo "5 大于 3"
fi
9. 快速决策指南 #
问自己三个问题,决定用哪个:
-
脚本需要在 sh(POSIX)环境下运行吗?
- 是 → 用
[ ]或test - 否 → 用
[[ ]](更推荐)
- 是 → 用
-
需要正则表达式或通配符匹配吗?
- 是 → 用
[[ ]] - 否 → 随意
- 是 → 用
-
有复杂的逻辑运算(多个条件)吗?
- 是 → 用
[[ ]](&&、||更清晰) - 否 → 随意
- 是 → 用
我的建议:
- 写新的 Bash 脚本:一律用
[[ ]],更安全、功能更强 - 维护旧的 POSIX 脚本:保留
[ ]或test - Dockerfile / 系统初始化脚本:用
[ ]保证兼容性
10. 总结 #
| 要点 | 说明 |
|---|---|
test |
原始命令,静默返回退出码 |
[ ] |
test 的别名,POSIX 兼容,必须加空格 |
[[ ]] |
Bash 增强版,功能强大,推荐使用 |
$? |
查看上一条命令的退出码 |
| 空格 | [ 和 ] 前后必须有空格 |
| 引号 | [ ] 中变量必须加引号,[[ ]] 中可选但建议加 |
| 退出码 0 | 表示成功(条件为真) |
| 退出码 非0 | 表示失败(条件为假) |
希望这份教程能帮你彻底掌握 Shell 中的条件判断!如果还有疑问,欢迎随时提问。😊