Test

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. 快速决策指南 #

问自己三个问题,决定用哪个:

  1. 脚本需要在 sh(POSIX)环境下运行吗?

    • 是 → 用 [ ]test
    • 否 → 用 [[ ]](更推荐)
  2. 需要正则表达式或通配符匹配吗?

    • 是 → 用 [[ ]]
    • 否 → 随意
  3. 有复杂的逻辑运算(多个条件)吗?

    • 是 → 用 [[ ]]&&|| 更清晰)
    • 否 → 随意

我的建议

  • 写新的 Bash 脚本:一律用 [[ ]],更安全、功能更强
  • 维护旧的 POSIX 脚本:保留 [ ]test
  • Dockerfile / 系统初始化脚本:用 [ ] 保证兼容性

10. 总结 #

要点 说明
test 原始命令,静默返回退出码
[ ] test 的别名,POSIX 兼容,必须加空格
[[ ]] Bash 增强版,功能强大,推荐使用
$? 查看上一条命令的退出码
空格 [] 前后必须有空格
引号 [ ] 中变量必须加引号[[ ]] 中可选但建议加
退出码 0 表示成功(条件为真)
退出码 非0 表示失败(条件为假)

希望这份教程能帮你彻底掌握 Shell 中的条件判断!如果还有疑问,欢迎随时提问。😊