shell 字符串大小的比较原理,以及 [ ] 与 [[ ]] 的本质区别

字符串比较大小的原理:逐个比较字符的 ASCII 或 Unicode 码点,一旦分出胜负就结束


字符串比较常用运算符:(在 [ ] 或 [[ ]] 中使用的字符串比较运算符)

  • =、==、!=、<、>、=~

  • -z:字符串长度为 0

  • -n:字符串长度不为 0

注意事项

  • < 和 > 在 [ ] 中使用时 必须带 引号 或 转义,否则会被 shell 当作重定向符号

  • = 和 == 是等价的,在 [ ] 和 [[ ]] 都可以使用。


一般 shell 脚本中的字符串比较都是通过 [ ] 或 [[ ]] 来完成,两者的区别在于

  • [] 是一个命令,是 test 的别名

  • [[ ]] 是一个关键字,在解析阶段就会被 shell 解释和处理


shell 的生命周期如下

shell 的关键字 和 命令在 shell 的生命周期中先后顺序不同,所以要先理解 shell 的生命周期,才能深入理解 shell 中 [ ] 和 [[ ]] 的区别。

  • 第一步:关键字解析,shell 根据内置的语法解析关键字,构建语法。如:识别到 [[ ]] 是一个关键字,它就知道里面的 < > 不是重定向,是比较运算符

  • 第二步:参数展开,如 变量展开$var、命令替换$(cmd),这些会把他们展开为实际的字符串形式。

  • 第三步:分词处理,如果字符串没有加引号,则按 $IFS 定义的分隔符来切割得到多个词

  • 第四步:路径扩展,进行分词处理后的字符串,如果里面有通配符,就会被通配符扩展为具体的文件名

  • 第五步:移除引号,如果字符串是使用 单引号 或 双引号 保护的,这个步骤会把引号移除掉。

  • 第六步:查找命令,如果是内部命令,直接执行,如果是外部命令,根据 PATH 变量从左到右找。

  • 第七步:执行命令,若存在字符串参数,就将解析后的字符串做为参数传递给这个命令。

  • 有双单引号保护:完全跳过步骤 1、步骤 2、步骤 3。因为单引号关会闭所有形式的展开,字符串会被完全原样保留。
  • 有双引号保护:完全跳过步骤2、步骤3。双引号下允许参数展开(变量、命令替换、算术),但是通配符会失效,也不会按 IFS 进行分词,所以空格、换行等会被保留。

例如:ls *.tar.gz 的完整执行过程

  • 第一步:没有关键字,跳过

  • 第二步:没有需要进行展开的内容,跳过。

  • 第三步:*.tar.gz 虽然没有引号,但是耶没有 IFS 字符,不会被切分。

  • 第四步: 有通配符,每引号,通配符启动路径扩展,会扫描当前目录,把所有 .tar.gz 文件替换成:apache.tar.gz data.tar.gz semms.tar.gz

  • 第五步:此处无引号,不存在引号移除,所以跳过。

  • 第六步:在 /usr/bin/ls 下找到可执行文件ls

  • 第七步:将处理后的字符串做为ls的参数传递,执行 ls apache.tar.gz data.tar.gz semms.tar.gz


shell 中 [ ] 和 [[ ]] 的具体区别

  • 区别1:[ ] 中使用 > 或 < 符号时,需要使用转义字符或加引号,在[[ ]] 中使用不用。

  • 区别2:[[ ]] 支持正则表达式,使用 =~ 可以写正则表达式,默认就支持扩展正则。例如:[[ "abc123" =~ [a-z]+[0-9]+ ]]

  • 区别3:[[ ]] 中的变量或命令替换后,替换后的字符串原样输出,不会进行IFS分词和通配符路径扩展。

  • 区别4:未加引号的通配符,在 [ ] [[ ]] 中的行为不通,[[ ]] 是模式匹配,[ ] 是通配符扩展。


为什么 [ ] 需要转义字符,而 [[ ]] 不需要要?

  • [[ ]] 是 Shell 的语法结构(关键字),在解析阶段就知道 < > 是字符串比较符,不会当重定向处理。

  • [ ] 是命令(test 的别名),在执行前会经过重定向解析,因此 < > 会被当作重定向符,必须转义


模式匹配 和 路径扩展 有什么区别?

  • 模式匹配:匹配字符串,不会查找文件系统。如:[[ "file1" == file* ]]* 只是会表示任意字符,所以返回 true

  • 路径扩展:通配符路径扩展,会查找文件系统文件名。如:[ "file1" = file* ]file* 会拓展为当前路径下所有以 file 开头的文件名

posted on 2025-12-05 17:32  背对背依靠  阅读(2)  评论(0)    收藏  举报