🔍搜索⌘K

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 n2n1 等于 n2==[ $var -eq 10 ]
n1 -ne n2n1 不等于 n2!=[ $var -ne 10 ]
n1 -ge n2n1 大于等于 n2>=[ $var -ge 10 ]
n1 -gt n2n1 大于 n2>[ $var -gt 10 ]
n1 -le n2n1 小于等于 n2<=[ $var -le 10 ]
n1 -lt n2n1 小于 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 = str2str1 与 str2 相等(注意:是单等号)[ “$str” = “hello” ]
str1 != str2str1 与 str2 不相等[ “$str” != “hello” ]
str1 > str2str1 大于 str2(需转义 >,避免被当作重定向)[ “$str1” > “$str2” ]
str1 < str2str1 小于 str2(需转义 <)[ “$str1” < “$str2” ]
-n strstr 长度不为空(非 0)[ -n “$str” ]
-z strstr 长度为空(等于 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 filefile 存在且是目录[ -d “$file” ]
-e filefile 存在(无论类型是文件还是目录)[ -e “$file” ]
-f filefile 存在且是普通文件(非目录、设备文件等)[ -f “$file” ]
-r filefile 存在且当前用户有读权限[ -r “$file” ]
-w filefile 存在且当前用户有写权限[ -w “$file” ]
-x filefile 存在且当前用户有执行权限[ -x “$file” ]
-s filefile 存在且非空(文件大小 > 0)[ -s “$file” ]
-O filefile 存在且当前用户是文件的所有者[ -O “$file” ]
-G filefile 存在且文件的属组与当前用户的默认属组一致[ -G “$file” ]
file1 -nt file2file1 比 file2 新(修改时间更新)[ “$file1” -nt “$file2” ]
file1 -ot file2file1 比 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 命令的核心要点

  1. 本质定位:Shell 条件判断的「基石」,靠退出状态码(0 成功 / 非 0 失败)告诉外面条件成不成立;
  2. 两种语法:test 条件 和 [ 条件 ] 等价,后者更省事,但注意括号和条件之间留空格;
  3. 三大场景:
    • 数值比较:只认整数,用 -eq/-gt 这类运算符;
    • 字符串比较:记得加双引号,>/< 要转义,-n/-z 查空值;
    • 文件比较:最常用,覆盖存在性、权限、新旧等检测,是运维脚本的核心;
  4. 复合条件:用 &&(与)、||(或)把多个条件拼起来,就能写复杂逻辑;
  5. 避坑关键:变量加双引号、括号加空格、转义特殊符号、别拿浮点数来比。

test 命令就是 Shell 脚本的「逻辑骨架」,搞懂它之后,不管 if-then 的分支判断,还是 while/until 的循环控制,都能轻松拿下。建议结合真实场景多练练(比如「查日志文件在不在且可读」「判断用户输入是不是空的」),慢慢就养成条件判断的思路了。