shell 字符串比较(最佳实践)

shell 字符串比较:从入门到实战

在日常的 Shell 脚本开发中,我们经常需要判断两个字符串是否相等、哪个更大、是否为空,甚至是否包含特定子串。这些操作统称为 shell 字符串比较,是编写自动化脚本的基础能力之一。尤其当你在做系统运维、批量处理文件、配置校验时,熟练掌握字符串比较技巧,能让你事半功倍。

想象一下,你在写一个备份脚本,需要判断当前日期是否与配置文件中设定的备份周期一致。这个“判断是否一致”的过程,本质上就是一次字符串比较。如果连这个都搞不定,脚本就可能误操作,甚至覆盖重要数据。

接下来,我们就从最基础的语法开始,一步步带你掌握 shell 字符串比较的核心用法。


基本比较操作符详解

Shell 提供了多种用于字符串比较的操作符,它们主要分为两类:字符串比较文件属性比较。我们这里重点讲解字符串比较。

使用 =!= 进行相等性判断

最常用的字符串比较是判断两个字符串是否相等。在 Bash 中,使用 = 表示“相等”,!= 表示“不相等”。

#!/bin/bash

username="admin"
expected_user="admin"

if [ "$username" = "$expected_user" ]; then
    echo "✅ 用户名匹配,授权通过"
else
    echo "❌ 用户名不匹配,拒绝访问"
fi

⚠️ 注意:[ 是 test 命令的简写,必须与字符串之间有空格。
例如:[ $a = $b ] ❌ 错误,[ "$a" = "$b" ] ✅ 正确。
变量用双引号包裹,可以防止空值或含空格的字符串导致语法错误。

使用 ==!=(Bash 特有)

在 Bash 中,=== 功能相同,但更符合人类直觉。它主要用于 [[ ]] 结构中,推荐在现代脚本中使用。

#!/bin/bash

name="Alice"
if [[ "$name" == "Alice" ]]; then
    echo "欢迎,Alice!"
fi

if [[ "$name" != "Bob" ]]; then
    echo "你不是 Bob,欢迎访问"
fi

[[ ]][ ] 更安全,支持通配符、正则匹配,且不会因空值导致语法错误。
在大多数生产环境中,推荐优先使用 [[ ]]


字符串大小比较:<> 的陷阱

你可能会想:字符串也能比大小?当然可以,Shell 会按字典序(lexicographical order)比较字符串,也就是字母表顺序。

例如:apple < banana,因为 ab 前面。

但这里有个关键陷阱:<> 在 Shell 中是重定向符号,直接写会出错!

if [ "apple" < "banana" ]; then
    echo "apple 更小"
fi

上面的脚本会报错:bash: [: missing ]'。因为 Shell 把 <当作输入重定向,试图从文件banana` 读取内容。

正确做法:使用 \<\>

要让 Shell 把 <> 当作比较符号,必须用反斜杠转义:

#!/bin/bash

if [ "apple" \< "banana" ]; then
    echo "✅ apple 字典序小于 banana"
fi

if [ "zebra" \> "apple" ]; then
    echo "✅ zebra 字典序大于 apple"
fi

📌 提示:在 [ ] 中,< 必须写成 \<> 必须写成 \>
[[ ]] 中,不需要转义,可以直接使用 <>

if [[ "apple" < "banana" ]]; then
    echo "apple 在字典序中更靠前"
fi

字符串长度与空值判断

在实际脚本中,判断字符串是否为空或长度是否为 0 是非常常见的需求。

检查字符串是否为空

#!/bin/bash

message=""

if [ -z "$message" ]; then
    echo "⚠️ 消息为空,需要填写"
fi

if [ -n "$message" ]; then
    echo "消息不为空"
else
    echo "消息为空"
fi
  • -z:字符串为空时返回 true
  • -n:字符串非空时返回 true

小技巧:[ -z "$var" ] 等价于 [ "$var" = "" ],但更简洁、更安全。

判断字符串长度

虽然 Shell 没有直接的“长度比较”操作符,但我们可以结合 # 获取变量长度。

#!/bin/bash

password="123456"

length=${#password}

if [ $length -ge 8 ]; then
    echo "✅ 密码长度合格"
else
    echo "❌ 密码长度不足 8 位"
fi

${#var} 是 Bash 的字符串长度语法,返回变量 var 的字符数。


实际应用案例:用户输入校验脚本

我们来写一个完整的例子,演示如何在真实场景中组合使用多种字符串比较。

#!/bin/bash

echo "请输入用户名(仅支持字母和数字,长度 4-12):"
read username

if [ -z "$username" ]; then
    echo "❌ 错误:用户名不能为空"
    exit 1
fi

if [ ${#username} -lt 4 ] || [ ${#username} -gt 12 ]; then
    echo "❌ 错误:用户名长度必须在 4 到 12 位之间"
    exit 1
fi

if [[ ! "$username" =~ ^[a-zA-Z0-9]+$ ]]; then
    echo "❌ 错误:用户名只能包含字母和数字"
    exit 1
fi

reserved=("admin" "root" "guest")
found=false

for word in "${reserved[@]}"; do
    if [ "$username" = "$word" ]; then
        echo "❌ 错误:用户名 $username 是保留名,不可用"
        found=true
        break
    fi
done

if [ "$found" = true ]; then
    exit 1
fi

echo "🎉 用户名 $username 验证通过,可以使用"

这个脚本展示了:

  • 如何组合使用 [[ ]][[ =~ ]] 进行正则匹配
  • 如何用 for 循环遍历数组进行比对
  • 如何用 exit 1 提前终止脚本,避免错误继续执行

高级技巧:通配符与模式匹配

[[ ]] 支持通配符匹配,让你能轻松判断字符串是否符合某种模式。

#!/bin/bash

filename="document.pdf"

if [[ "$filename" == *.pdf ]]; then
    echo "这是一个 PDF 文件"
fi

if [[ "$filename" == doc* ]]; then
    echo "文件名以 doc 开头"
fi

if [[ "$filename" == *.txt || "$filename" == *.log ]]; then
    echo "这是文本或日志文件"
fi

* 表示任意字符(包括空字符)
? 表示单个字符
[abc] 表示匹配 a、b 或 c 中任意一个


常见错误与调试建议

在使用 shell 字符串比较时,有几个高频错误需要特别注意:

错误类型 原因 正确写法
[ "$a" < "$b" ] < 被当作重定向 [ "$a" \< "$b" ][[ "$a" < "$b" ]]
[ $a = $b ] 缺少引号 [ "$a" = "$b" ]
使用 ==[ ] 不支持 改用 = 或使用 [[ ]]
变量未初始化 空值导致语法错误 始终用双引号包裹变量

💡 调试建议:在脚本开头添加 set -euo pipefail,让脚本在错误时立即退出,避免潜藏问题。


总结与进阶建议

通过本文,你应该已经掌握了 shell 字符串比较的核心语法和常见陷阱。关键点总结如下:

  • 使用 [[ ]] 代替 [ ],更安全、功能更强
  • 字符串相等用 ===,不相等用 !=
  • 比较大小时,<> 必须转义为 \<\>,或在 [[ ]] 中直接使用
  • 判断空值用 -z,判断非空用 -n
  • 长度判断用 ${#var},结合 -ge-le 等整数比较
  • 实际项目中,结合正则、数组、循环,实现复杂逻辑

掌握这些技巧后,你已经具备了编写健壮 Shell 脚本的基础能力。下一步可以学习如何处理文件、解析 JSON、调用外部命令,逐步构建自动化运维系统。

记住:一个成熟的脚本,不是写得越多越好,而是判断得越准越好。每一次字符串比较,都是你对程序逻辑的一次确认。

愿你在 Shell 的世界里,写出简洁、可靠、可维护的代码。