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


35

两者之间有什么区别吗?

[[ $a == z* ]]

[ $a == z* ] 

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

此外,的工作与[[ ]]有何不同[ ]

Answers:


41

[[ … ]]和之间的区别[ … ]主要在于使用单括号或双括号bash。至关重要的是,它[[ … ]]是特殊的语法,而它[是命令的一个好看的名称。[[ … ]]对于里面的东西有特殊的语法规则[ … ]

随着通配符的增加,下面[[ $a == z* ]]是评估方法:

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

下面是如何[ $a == z* ]进行评估:

  1. 解析命令:这是[与通过评估的话形成的参数命令$a==z*]
  2. 展开$a为变量的值a
  3. 对命令的参数执行分词和文件名生成。
    • 例如,如果值a是6个字符的字符串foo b*(由例如获得a='foo b*')和文件的在当前目录(列表barbazquxzimzum),则扩展的结果是下列词语的列表:[foobarbaz==zimzum]
  4. [使用上一步中获得的参数运行命令。
    • 对于上面的示例值,该[命令抱怨语法错误并返回状态2。

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


39

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

[testUnix System III(1981)的别名,并且两者都内置在Shell中。尽管应该指出的是,某些Unix变体[直到此之后才有命令(直到2000年代早期,某些sh基于Almquist shell的BSD上才有命令(test内置ash源始终包含在s中,但是基于最初被禁用的BSD))。

请注意,testaka [是执行“测试”的命令,没有该命令执行的赋值,因此没有理由在赋值和相等运算符之间进行歧义,因此相等运算符为===仅受到的一些最新实现的支持[(并且只是的别名=)。

因为[只不过是一个命令,所以外壳程序会以与其他任何命令相同的方式来解析它。

具体来说,在您的示例中$a,由于未加引号,因此将根据常规的单词拆分规则将其拆分为多个单词,并且每个单词都将进行文件名生成(也称为“ globbing”),以产生可能更多的单词,每个单词导致[命令的单独参数。

同样,z*将扩展到当前目录中以开头的文件名列表z

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

[将其参数解析为条件表达式。这9个参数未加起来为有效的条件表达式,因此可能会返回错误。

[[ ... ]]构造体是Korn壳大约在1988年引入的,因为ksh86a1987年ksh88从一开始就没有这种构造。

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

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

[[ ... ]]==从一开始就知道并等于=1。尽管ksh的一个错误(并引起混乱和许多错误)是===不是一个相等运算符,而是一个模式匹配运算符(尽管可以用引号禁用匹配方面,但是使用因外壳而异的不清楚规则)。

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

通常,[[ ... ]][命令比用命令射击更难。但是一些规则,例如

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

[使用POSIX shell 使其可靠。

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

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


1个例外:[[...]]在版本中已添加到bash中2.02。在2.03更改之前,[[ x = '?' ]]返回true,而[[ x == '?' ]]返回false。引用并不能阻止=在那些版本中使用运算符时的模式匹配,但在使用时却可以==


很棒的信息。您能否解释一下为什么“从不使用-a或-o运算符”?
grebneke 2014年

@grebneke,尝试[ '!' = foo -o a = a ]bash为实例。
斯特凡Chazelas

0

两者都用于评估表达式,并且[[不适用于POSIX旧版本的bourn shell,并且[[也支持模式匹配和正则表达式。例子试试这些

[ $n -eq 0 -a $y -eq 0 ] && echo "Error" || echo "Ok"

[[ $n -eq 0 && $y -eq 0 ]] && echo "Error" || echo "Ok"


请注意,Bourne shell不是POSIX,[[...]]仅在某些shell的某些版本中[支持正则表达式,就像某些版本的do支持regexp匹配一样。对于arithemtic相比之下,在那些炮弹的支持[[,你宁愿写(( n == 0 && y == 0))
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.