Linux Shell test 命令详解:条件判断的核心工具
概述
在 Shell 脚本中,test 命令是实现 “条件判断” 的核心工具 —— 它能检测变量、字符串、文件等是否满足特定条件,并通过 “退出状态码” 告诉后续逻辑(如 if-then)是否执行。
简单来说,test 命令的核心作用是:
“判断一个条件是否成立,成立则返回 0(成功),不成立则返回非 0(失败)”
这种特性让 test 成为 if-then、while、until 等流程控制语句的 “搭档”,是编写逻辑复杂脚本的基础。
一、基础语法:test 命令的两种写法
test 命令有两种等价的使用形式,核心功能完全一致,仅语法风格不同,可根据习惯选择。
1. 标准写法:显式使用 test 关键字
# 格式:test 条件
test condition
# 结合 if-then 使用(最常见场景)
if test condition
then
# 条件成立时执行的命令
命令1
命令2
fi
2. 简化写法:用方括号 [ ] 替代 test
Bash Shell 提供了更简洁的语法,用 [ ] 包裹条件,本质是 test 命令的别名:
# 格式:[ 条件 ](注意:括号与条件间必须有空格!)
[ condition ]
# 结合 if-then 使用
if [ condition ]
then
# 条件成立时执行的命令
命令1
命令2
fi
关键注意点:方括号的空格要求
[ condition ] 中,第一个 [ 后、最后一个 ] 前必须加空格,否则 Shell 会将 [condition] 识别为 “普通命令”,导致语法错误。例如:
- 正确:[ $var -eq 10 ](括号与条件间有空格)
- 错误:[$var -eq 10](无空格,会报错 “命令未找到”)
二、无条件测试:test 命令的默认行为
如果 test 命令未指定任何条件(即 test 后无参数,或 [ ] 内为空),它会默认返回 非 0 状态码(条件不成立),可用于检测 “是否漏写条件”。
示例:无参数的 test 命令
#!/bin/bash
# 测试无条件的 test 命令
if test # 等价于 if [ ]
then
echo "No expression returns a True" # 条件成立时执行(不会触发)
else
echo "No expression returns a False" # 条件不成立时执行(会触发)
fi
执行效果:
No expression returns a False
三、test 命令的三类核心测试场景
test 命令支持三大类条件测试,覆盖 Shell 脚本中 90% 以上的判断需求:数值比较、字符串比较、文件比较。下面逐一详解其语法、示例和注意事项。
1. 数值比较:仅支持整数,不支持浮点数
数值比较用于判断两个整数的大小关系(如 “大于”“等于”“小于”),仅支持整数,浮点数会导致语法错误。
常用数值比较运算符
运算符 | 含义 | 等价数学符号 | 示例(判断 var 是否等于 10) |
---|---|---|---|
n1 -eq n2 | n1 等于 n2 | == | [ $var -eq 10 ] |
n1 -ne n2 | n1 不等于 n2 | != | [ $var -ne 10 ] |
n1 -ge n2 | n1 大于等于 n2 | >= | [ $var -ge 10 ] |
n1 -gt n2 | n1 大于 n2 | > | [ $var -gt 10 ] |
n1 -le n2 | n1 小于等于 n2 | <= | [ $var -le 10 ] |
n1 -lt n2 | n1 小于 n2 | < | [ $var -lt 10 ] |
示例:数值比较的实际使用
#!/bin/bash
value1=10
value2=11
# 测试 value1 是否大于 5
if [ $value1 -gt 5 ]
then
echo "✅ The test value $value1 is greater than 5."
fi
# 测试 value1 是否等于 value2
if [ $value1 -eq $value2 ]
then
echo "✅ The values are equal."
else
echo "❌ The values are different."
fi
执行效果:
✅ The test value 10 is greater than 5.
❌ The values are different.
注意事项
- 仅支持整数: 若使用浮点数(如 value1=10.5),会报错 “整数表达式预期”;
- 变量引用加双引号: 建议将变量用双引号包裹(如 [ "$value1" -gt 5 ]),避免变量为空时导致语法错误。
2. 字符串比较:处理文本内容的判断
字符串比较用于判断两个字符串的 “相等性”“大小关系” 或 “是否为空”,是处理用户输入、配置参数的核心场景。
常用字符串比较运算符
运算符 | 含义 | 示例(判断 str 是否等于 "hello") |
---|---|---|
str1 = str2 | str1 与 str2 相等(注意:是单等号) | [ "$str" = "hello" ] |
str1 != str2 | str1 与 str2 不相等 | [ "$str" != "hello" ] |
str1 > str2 | str1 大于 str2(需转义 >,避免被当作重定向) | [ "$str1" > "$str2" ] |
str1 < str2 | str1 小于 str2(需转义 <) | [ "$str1" < "$str2" ] |
-n str | str 长度不为空(非 0) | [ -n "$str" ] |
-z str | str 长度为空(等于 0) | [ -z "$str" ] |
示例 1:字符串相等 / 不相等判断
#!/bin/bash
testuser=roger
# 测试字符串相等
if [ "$testuser" = "roger" ]
then
echo "✅ The testuser variable contains: roger"
else
echo "❌ The testuser variable contains: $testuser"
fi
# 测试字符串不相等
testuser=rich
if [ "$testuser" != "roger" ]
then
echo "✅ The testuser variable does NOT contain: roger"
else
echo "❌ The testuser variable contains: roger"
fi
执行效果:
✅ The testuser variable contains: roger
✅ The testuser variable does NOT contain: roger
示例 2:字符串大小比较(需转义特殊符号)
Shell 中 > 和 < 是 “重定向符号”,用于字符串比较时必须用 \ 转义,否则会被误解析为文件操作。
#!/bin/bash
string1=soccer
string2=zorbfootball
# 错误写法:未转义 >,会被当作重定向,创建 zorbfootball 文件
# if [ $string1 > $string2 ]
# 正确写法:转义 >,实现字符串比较
if [ "$string1" \> "$string2" ]
then
echo "$string1 is greater than $string2"
else
echo "$string1 is less than $string2"
fi
执行效果:
soccer is less than zorbfootball
字符串大小比较的规则
字符串比较基于 “字符的 Unicode 编码值” 逐位比较:
- 小写字母编码:a(97) < b(98) < ... < s(115) < ... < z(122)
- 示例中 soccer 的第一个字符 s(115) 小于 zorbfootball 的第一个字符 z(122),因此 soccer < zorbfootball。
示例 3:检测字符串是否为空
-n 和 -z 是检测字符串空值的常用运算符,尤其适合判断 “用户是否输入了内容”:
#!/bin/bash
string1=Soccer # 非空字符串
string2='' # 空字符串(显式赋值为空)
# string3 未定义(默认为空)
# 测试 -n:字符串非空
if [ -n "$string1" ]
then
echo "✅ The string '$string1' is NOT empty"
else
echo "❌ The string '$string1' IS empty"
fi
# 测试 -z:字符串为空
if [ -z "$string2" ]
then
echo "✅ The string '$string2' IS empty"
else
echo "❌ The string '$string2' is NOT empty"
fi
# 测试未定义变量(默认为空)
if [ -z "$string3" ]
then
echo "✅ The string '$string3' IS empty"
else
echo "❌ The string '$string3' is NOT empty"
fi
执行效果:
✅ The string 'Soccer' is NOT empty
✅ The string '' IS empty
✅ The string '' IS empty
字符串比较的关键注意点
- 变量必须加双引号:若变量为空且不加双引号(如 [ $str = "hello" ]),会变成 [ = "hello" ],导致语法错误;加双引号后([ "$str" = "hello" ]),会正确解析为 [ "" = "hello" ];
- = 是单等号:字符串相等比较用 =(单等号),而非 ==(部分 Shell 支持 ==,但 = 是标准写法,兼容性更好);
- 转义特殊符号:> 和 < 必须转义为 > 和 <,避免被当作重定向。
3. 文件比较:Linux 文件系统的状态检测
文件比较是 test 命令最强大、最常用的功能 —— 它能检测文件 / 目录的 “存在性”“类型”“权限”“新旧程度” 等,是运维脚本的核心工具(如 “检查日志文件是否存在”“判断脚本是否可执行”)。
常用文件比较运算符
运算符 | 含义 | 示例(判断 file 是否为目录) |
---|---|---|
-d file | file 存在且是目录 | [ -d "$file" ] |
-e file | file 存在(无论类型是文件还是目录) | [ -e "$file" ] |
-f file | file 存在且是普通文件(非目录、设备文件等) | [ -f "$file" ] |
-r file | file 存在且当前用户有读权限 | [ -r "$file" ] |
-w file | file 存在且当前用户有写权限 | [ -w "$file" ] |
-x file | file 存在且当前用户有执行权限 | [ -x "$file" ] |
-s file | file 存在且非空(文件大小 > 0) | [ -s "$file" ] |
-O file | file 存在且当前用户是文件的所有者 | [ -O "$file" ] |
-G file | file 存在且文件的属组与当前用户的默认属组一致 | [ -G "$file" ] |
file1 -nt file2 | file1 比 file2 新(修改时间更新) | [ "$file1" -nt "$file2" ] |
file1 -ot file2 | file1 比 file2 旧(修改时间更早) | [ "$file1" -ot "$file2" ] |
下面通过 8 个实战示例,掌握文件比较的核心用法:
示例 1:检查目录是否存在(-d)
在 “切换目录” 或 “向目录写文件” 前,先检查目录是否存在,避免脚本报错:
#!/bin/bash
jump_dir=/home/roger
# 检查目录是否存在
if [ -d "$jump_dir" ]
then
echo "✅ The $jump_dir directory exists. Entering..."
cd "$jump_dir" || exit # 切换目录,失败则退出脚本
ls -l # 列出目录内容
else
echo "❌ The $jump_dir directory does NOT exist."
fi
执行效果(目录不存在时):
❌ The /home/roger directory does NOT exist.
示例 2:检查文件 / 目录是否存在(-e)
-e 是 “通用存在性检测”,无论目标是文件还是目录,只要存在就返回成立:
#!/bin/bash
location=$HOME # 当前用户的家目录(肯定存在)
file_name="test.log" # 待检测的文件
# 先检查目录是否存在
if [ -d "$location" ]
then
echo "✅ Directory $location exists."
# 再检查文件是否存在
if [ -e "$location/$file_name" ]
then
echo "✅ File $location/$file_name exists. Updating..."
date >> "$location/$file_name" # 向文件追加当前时间
else
echo "❌ File $location/$file_name does NOT exist. Nothing to do."
fi
else
echo "❌ Directory $location does NOT exist. Nothing to do."
fi
执行效果(文件不存在时):
✅ Directory /home/yourname exists.
❌ File /home/yourname/test.log does NOT exist. Nothing to do.
示例 3:检查是否为普通文件(-f)
-e 会同时匹配文件和目录,若需明确 “目标是文件”,需用 -f 检测:
#!/bin/bash
object_name=$HOME # 家目录(是目录,非文件)
echo "Checking object: $object_name"
if [ -e "$object_name" ]
then
echo "✅ Object $object_name exists."
# 进一步判断是文件还是目录
if [ -f "$object_name" ]
then
echo "✅ And it's a regular file."
else
echo "✅ And it's a directory."
fi
else
echo "❌ Object $object_name does NOT exist."
fi
执行效果:
Checking object: /home/yourname
✅ Object /home/yourname exists.
✅ And it's a directory.
示例 4:检查文件是否可读(-r)
在 “读取文件内容” 前,先检查当前用户是否有读权限,避免 “权限被拒” 错误:
#!/bin/bash
pwfile=/etc/shadow # 系统密码文件(普通用户无读权限)
echo "Checking read access to $pwfile..."
if [ -f "$pwfile" ]
then
# 先确认是文件,再检查读权限
if [ -r "$pwfile" ]
then
echo "✅ Read access allowed. Showing last 3 lines..."
tail -3 "$pwfile"
else
echo "❌ Read access denied (normal for regular users)."
fi
else
echo "❌ File $pwfile does NOT exist."
fi
执行效果(普通用户执行):
Checking read access to /etc/shadow...
❌ Read access denied (normal for regular users).
示例 5:检查文件是否非空(-s)
-s 用于检测文件是否 “存在且非空”(文件大小 > 0),常用于 “避免删除有内容的文件”:
#!/bin/bash
file_name=$HOME/test.log
echo "Checking if $file_name is empty..."
if [ -f "$file_name" ]
then
if [ -s "$file_name" ]
then
echo "✅ File $file_name exists and has content. Will NOT delete."
else
echo "❌ File
file_nameexistsbutisempty.Deleting..."rm"
file_name" # 删除空文件
fi
else
echo "❌ File $file_name does NOT exist."
fi
执行效果(文件非空时):
Checking if /home/yourname/test.log is empty...
✅ File /home/yourname/test.log exists and has content. Will NOT delete.
示例 6:检查文件是否可写(-w)
在“向文件写入内容”前,用 -w
检测写权限,避免“无法写入”错误:
#!/bin/bash
item_name=$HOME/test.log
echo "Checking write access to $item_name..."
if [ -f "$item_name" ]
then
if [ -w "$item_name" ]
then
echo "✅ Write access allowed. Appending current time..."
date +"%Y-%m-%d %H:%M:%S" >> "$item_name" # 追加时间戳
else
echo "❌ Write access denied."
fi
else
echo "❌ File $item_name does NOT exist or is not a file."
fi
**执行效果(有写权限时)**:
```plaintext
Checking write access to /home/yourname/test.log...
✅ Write access allowed. Appending current time...
示例 7:检查文件是否可执行(-x)
-x 用于检测脚本或程序是否有 “执行权限”,是 “运行外部程序” 前的必要检查:
#!/bin/bash
script_path=$HOME/scripts/backup.sh # 待检测的脚本
echo "Checking if $script_path is executable..."
if [ -x "$script_path" ]
then
echo "✅ Executable. Running the script..."
"$script_path" # 执行脚本
else
echo "❌ Not executable. Try 'chmod +x $script_path' to add permission."
fi
执行效果(无执行权限时):
Checking if /home/yourname/scripts/backup.sh is executable...
❌ Not executable. Try 'chmod +x /home/yourname/scripts/backup.sh' to add permission.
示例 8:比较两个文件的新旧(-nt /-ot)
-nt(newer than)和 -ot(older than)用于比较文件的 “修改时间”,常用于 “判断是否需要更新文件”(如安装包更新):
#!/bin/bash
download_rpm=$HOME/Downloads/games.rpm # 新下载的安装包
installed_rpm=$HOME/software/games.rpm # 已安装的旧包
if [ "$download_rpm" -nt "$installed_rpm" ]
then
echo "✅ $download_rpm is newer. Suggest updating."
else
echo "❌ $download_rpm is older or same. No need to update."
fi
执行效果(新包更新时):
✅ /home/yourname/Downloads/games.rpm is newer. Suggest updating.
四、复合条件测试:组合多个判断条件
实际脚本中,常需要 “同时满足多个条件” 或 “满足任意一个条件” 才执行命令,此时需用 布尔运算符 组合多个 test 条件。
1. 两种布尔运算符
Shell 支持两种核心布尔逻辑,语法格式如下:
逻辑关系 | 运算符 | 语法格式 | 含义 |
---|---|---|---|
逻辑与 | && | [ 条件 1 ] && [ 条件 2 ] | 两个条件同时成立,整体才成立 |
逻辑或 | || | [条件 1] || [条件 2] | 两个条件任意一个成立,整体就成立 |
2. 示例 1:逻辑与(&&)—— 同时满足两个条件
需求:“目录存在” 且 “文件可写” 时,才向文件写入内容:
#!/bin/bash
dir=$HOME/logs
file=$dir/app.log
# 逻辑与:目录存在 && 文件可写
if [ -d "$dir" ] && [ -w "$file" ]
then
echo "✅ Directory exists and file is writable. Appending log..."
echo "[$(date +%H:%M:%S)] App log content" >> "$file"
else
echo "❌ Either directory does not exist or file is not writable."
fi
执行效果(两个条件都成立时):
✅ Directory exists and file is writable. Appending log...
3. 示例 2:逻辑或(||)—— 满足任意一个条件
需求:“文件不存在” 或 “文件为空” 时,创建新文件并写入初始内容:
#!/bin/bash
file=$HOME/config.ini
# 逻辑或:文件不存在 || 文件为空
if [ ! -e "$file" ] || [ -z "$(cat "$file")" ] # ! 表示“非”,否定条件
then
echo "✅ File does not exist or is empty. Creating new file..."
echo "timeout=30" > "$file" # 写入初始配置
else
echo "❌ File exists and has content. Nothing to do."
fi
执行效果(文件不存在时):
✅ File does not exist or is empty. Creating new file...
五、常见问题与避坑指南
使用 test 命令时,新手易因细节忽略导致逻辑错误,以下是高频问题的解决方案:
问题现象 | 根本原因 | 解决方案 |
---|---|---|
方括号语法报错 “命令未找到” | [ 后或 ] 前未加空格(如 [$var -eq 10]) | 在括号与条件间加空格,正确格式:[ $var -eq 10 ] |
字符串比较时变量为空导致语法错误 | 变量未加双引号(如 [ $str = "hello" ],$str 为空时变成 [ = "hello" ]) | 所有变量引用加双引号,正确格式:[ "$str" = "hello" ] |
字符串大小比较时创建空文件 | 未转义 > 或 <(如 [ $str1 > $str2 ],> 被当作重定向) | 转义特殊符号,正确格式:[ "$str1" > "$str2" ] |
数值比较时浮点数报错 “整数表达式预期” | test 仅支持整数,不支持浮点数(如 [ 10.5 -gt 5 ]) | 若需处理浮点数,改用 bc 命令计算(如 if (($(echo"10.5> 5" | bc) )); then ...`) |
文件比较时路径含空格导致错误 | 路径变量未加双引号(如 [ -f $HOME/my file.txt ],被拆分为 [ -f /home/yourname/my file.txt ]) | 路径变量加双引号,正确格式:[ -f "$HOME/my file.txt" ] |
六、总结:test 命令的核心要点
- 本质定位:Shell 条件判断的 “基石”,通过退出状态码(0 成功 / 非 0 失败)告知条件是否成立;
- 两种语法:test 条件 和 [ 条件 ] 等价,后者更简洁,但需注意括号与条件间的空格;
- 三大场景:
- 数值比较:仅支持整数,用 -eq/-gt 等运算符;
- 字符串比较:需加双引号,>/< 需转义,-n/-z 检测空值;
- 文件比较:最常用,覆盖存在性、权限、新旧等检测,是运维脚本核心;
- 复合条件:用 &&(与)、||(或)组合多个条件,实现复杂逻辑;
- 避坑关键:变量加双引号、括号加空格、转义特殊符号、不处理浮点数。
test 命令是 Shell 脚本的 “逻辑骨架”,掌握它后,无论是 if-then 的分支判断,还是 while/until 的循环控制,都能轻松实现。建议结合实际场景多练习(如 “检查日志文件是否存在且可读”“判断用户输入是否为空”),逐步形成条件判断的思维习惯。