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开头的文件名
浙公网安备 33010602011771号