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 给了个更省事的语法,用 [ ] 把条件裹起来,本质上就是 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_name exists but is empty. 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 的循环控制,都能轻松拿下。建议结合真实场景多练练(比如「查日志文件在不在且可读」「判断用户输入是不是空的」),慢慢就养成条件判断的思路了。