[[ $a == z* ]] 和 [ $a == z* ] 有什么区别?

mun*_*ish 39 bash shell-script wildcards test

这两者有什么区别吗。

[[ $a == z* ]]
Run Code Online (Sandbox Code Playgroud)

[ $a == z* ] 
Run Code Online (Sandbox Code Playgroud)

我可以举一个例子,他们会有不同的输出吗?

此外, 的工作方式[[ ]][ ]?

Gil*_*il' 44

[[ … ]]和之间的区别[ … ]主要在为什么带空格的参数扩展不带引号在双括号“[[”内起作用,但在单括号“[”内不起作用?. 至关重要的是,[[ … ]]是特殊的语法,而[是一个看起来很有趣的命令名称。[[ … ]]对里面的东西有特殊的语法规则,[ … ]没有。

加上通配符的皱纹,这里是如何[[ $a == z* ]]评估的:

  1. 解析命令:这是[[ … ]]围绕条件表达式的条件构造$a == z*
  2. 解析条件表达式:这是==二元运算符,带有操作数$az*
  3. 将第一个操作数展开为变量的值a
  4. 评估==运算符:测试变量的值是否a与模式匹配z*
  5. 评估条件表达式:它的结果是条件运算符的结果。
  6. 现在对命令进行评估,如果条件表达式为真,则其状态为 0,如果为假,则其状态为 1。

以下[ $a == z* ]是评估方法:

  1. 解析命令:这是[带有通过评估单词$a, ==, z*,形成的参数的命令]
  2. 展开$a为变量的值a
  3. 对命令的参数执行分词和文件名生成。
    • 例如,如果 的值为a6 个字符的字符串foo b*(由 eg 获得a='foo b*'),当前目录中的文件列表为(bar, baz, qux, zim, zum),则展开的结果是以下单词列表:[, foo, bar, baz, ==, zim, zum, ].
  4. [使用上一步中获得的参数运行命令。
    • 对于上面的示例值,该[命令会报告语法错误并返回状态 2。

注意:在[[ $a == z* ]]第 3 步中, 的值a不会进行分词和文件名生成,因为它位于需要单个单词的上下文中(条件运算符的左侧参数==)。在大多数情况下,如果一个单词在那个位置有意义,那么变量扩展的行为就像在双引号中一样。但是,该规则有一个例外: in [[ abc == $a ]],如果 的值a包含通配符,则abc与通配符模式匹配。例如,如果值aa*[[ abc == $a ]]是真(因为通配符*从的无引号膨胀来$a匹配bc),而[[ abc == "$a" ]]是假的(因为普通字符*来自引用的扩展$a不匹配bc)。在 里面[[ … ]],双引号没有区别,除了在字符串匹配运算符===!==~的右侧


Sté*_*las 43

[test命令的别名。Unix 第 6 版有一个if命令,但第 7 版(1979 年)带有新的Bourne shell,它有一些编程结构,包括 if-then-else-elif-fi 结构,而 Unix 第 7 版添加了一个test命令来执行大部分if在旧版本中由命令执行的“测试” 。

[test在 Unix System III (1981) 中被用作别名并且两者都被内置到 shell 中。尽管应该指出的是,一些 Unix 变体[直到在那之后很久才具有命令(直到2000 年代初,在一些sh基于 Almquist shell 的BSD上test内置函数一直包含在ash的源中,但在那些BSD 最初是禁用的))。

请注意,testaka[是一个执行“测试”的命令,该命令没有赋值,因此没有理由消除赋值和相等运算符之间的歧义,因此相等运算符是=. ==仅支持[(并且只是 的别名=)的几个最近的实现。

因为[它只不过是一个命令,所以 shell 对它的解析方式与任何其他命令相同。

具体来说,在您的示例中,$a,因为它没有被引用,将根据通常的分词规则被分成几个词,并且每个词都将经历文件名生成又名通配以产生可能更多的词,每个词导致[命令的单独参数。

同样,z*将扩展为当前目录中以z.

因此,举例来说,如果$ab* = x,有z1z2b1以及b2文件在当前目录中,该[命令将获得9个参数:[b1b2=x==z1z2]

[将其参数解析为条件表达式。这 9 个参数加起来不是一个有效的条件表达式,所以它可能会返回一个错误。

[[ ... ]]构造可能是在 1988 年左右由 Korn shell 引入的,因为ksh86a1987 年ksh88从一开始就没有它。

除了 ksh(所有实现),[[...]]bash(自 2.02 版起)和 zsh 也支持,但所有三个实现都不同,并且同一个 shell 的每个版本之间存在差异,尽管这些更改通常是向后兼容的(一个显着的例外是 bash 的=~已经知道在某个版本之后当其行为改变时会破坏一些脚本的运算符)。[[...]]不是由 POSIX、Unix 或 Linux (LSB) 指定的。它已经被考虑过几次,但没有被包括在内,因为主要 shell 支持的它的通用功能已经被[命令和case-in-esac构造所覆盖。

整个[[ ... ]]结构构成一个命令。也就是说,它有一个退出状态(这是它最重要的资产,因为它是评估条件表达式的结果),您可以将其通过管道传递给另一个命令(尽管它没有用),并且通常在任何地方使用它使用任何其他命令(仅在 shell 内部,因为它是一个 shell 构造)但它不像普通的简单命令那样被解析。里面的内容被 shell解析为条件表达式,分词和文件名生成的常用规则有所不同。

[[ ... ]]==从一开始就知道,相当于=1。ksh 的一个错误(并导致混淆和许多错误)是=and==不是相等运算符,而是模式匹配运算符(尽管可以通过引用禁用匹配方面,但由于不同 shell 的规则不明确)。

在上面的代码中[[ $a == z* ]],shell 会将其解析为类似于通常的规则中的几个标记,将其识别为模式匹配比较,将其z*视为与a变量内容匹配的模式。

一般来说,用脚开枪[[ ... ]]比用[命令更难。但是有一些规则,比如

  • 总是引用变量
  • 永远不要使用-aor-o运算符(使用多个[命令和&&and || shell运算符)

[与POSIX壳可靠。

[[...]]在不同的 shell 中支持额外的运算符,如-nt, regexp 匹配运算符...但列表和行为因 shell 和版本而异。

因此,除非您知道脚本将被解释的 shell 和最低版本,否则坚持使用标准[命令可能更安全。


1一个例外:[[...]]在 bash 版本中被添加2.02。直到2.03它被改变,[[ x = '?' ]]而将返回true[[ x == '?' ]]将返回false。也就是说,=在这些版本中使用运算符时,引用并没有阻止模式匹配,但在使用==.