为什么 `[` 是 shell 内建的,而 `[[` 是 shell 关键字?

Cit*_*ght 69 shell shell-builtin

据我所知,[[是 的增强版本[,但是当我将其[[视为关键字并[显示为内置时,我感到困惑。

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword
Run Code Online (Sandbox Code Playgroud)

TLDP

内置命令可能是同名系统命令的同义词,但 Bash 在内部重新实现了它。例如,Bash echo 命令与 /bin/echo 不同,尽管它们的行为几乎相同。

关键字是保留字、标记或运算符。关键字对 shell 有特殊的意义,实际上是 shell 语法的构建块。例如,for、while、do 和 ! 是关键字。与内置函数类似,关键字被硬编码到 Bash 中,但与内置函数不同的是,关键字本身不是命令,而是命令结构的子单元。[2]

难道不应该使双方[[[关键字?有什么我在这里想念的吗?此外,此链接再次确认两者[[[应该属于同一类。

Joh*_*024 85

[和之间的区别[[是非常根本的。

  • [是一个命令。它的参数的处理方式与处理任何其他命令参数的方式相同。例如,考虑:

    [ -z $name ]
    
    Run Code Online (Sandbox Code Playgroud)

    外壳将扩大$name并进行双方对结果分词和文件名的产生,只是因为它会为任何其他命令。

    例如,以下将失败:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments
    
    Run Code Online (Sandbox Code Playgroud)

    为了使这项工作正常进行,引号是必要的:

    $ [ -n "$name" ] && echo not empty
    not empty
    
    Run Code Online (Sandbox Code Playgroud)
  • [[是一个 shell 关键字,它的参数根据特殊规则进行处理。例如,考虑:

    [[ -z $name ]]
    
    Run Code Online (Sandbox Code Playgroud)

    外壳将扩大$name,但是,不同于其他任何命令,它将执行没有分词,也没有对结果文件名生成。例如,尽管在 中嵌入了空格,但以下内容仍会成功name

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty
    
    Run Code Online (Sandbox Code Playgroud)

概括

[ 是一个命令,与 shell 执行的所有其他命令遵循相同的规则。

因为[[是关键字,而不是命令,shell 对其进行了特殊处理,并且在非常不同的规则下运行。


War*_*ung 63

V7 Unix中——Bourne shell 首次亮相的地方——[被称为test,它仅作为/bin/test. 所以,你今天要写的代码是:

if [ "$foo" = "bar" ] ; then ...
Run Code Online (Sandbox Code Playgroud)

你会写成

if test "$foo" = "bar" ; then ...
Run Code Online (Sandbox Code Playgroud)

这第二个符号仍然存在,我发现它更清楚发生了什么:您正在调用一个名为 的命令test,该命令评估其参数并返回一个退出状态代码if用于决定下一步做什么。该命令可能内置在 shell 中,也可能是一个外部程序。¹

[作为test后来的替代。² 它可能是 的内置同义词test,但它也作为/bin/[现代系统提供,用于没有内置它的 shell。

[并且test可以使用相同的代码实现。对于OS X/bin/[/bin/test在 OS X 上都是这种情况,其中这些是指向同一可执行文件的硬链接。³ 因此,实现完全忽略了尾随]:如果您将其称为/bin/[,则不需要它,并且它不会抱怨如果你做的是提供/bin/test

这段历史没有任何影响[[,因为从来没有一个原始程序被调用[[。它纯粹存在于那些将它实现为POSIX shell扩展的shell 中

“内置”和“关键字”之间的部分区别是由于这段历史。它还反映了解析[[表达式的语法规则不同的事实,如John1024 的回答.?


脚注:

  1. 当你这样看待它时,它清楚地表明为什么你必须[在 shell 脚本中放置空格,这与大多数其他编程语言中的括号和方括号的工作方式不同。如果 shell 的命令解析器允许if["$x"...,它也必须允许iftest"$x"...

  2. 它发生在 1980 年左右。/bin/[在我1979 年的古代 Unix V7副本中不存在,也没有将其man test记录为别名。在相应的手册页条目我在一个预发布副本系统III从1980年手册,它上市。

  3. ls -i /bin/[ /bin/test

  4. 但不要指望这种行为。在Bash内建的版本[确实需要关闭],并且其内置的test实施将抱怨,如果你提供。

  5. 内置命令与外部命令的区别可能还有另一个原因:这两种实现的行为可能不同。echo许多系统都是这种情况。因为只有一种实现,所以不需要对关键字进行这样的区分。

  • @sree `[[` 作为关键字允许 bash 执行使用 `[` 无法执行的操作 - 例如,很多时候不需要引用,因为 shell 知道它是一个变量。也就是说,当使用关键字时,命令行的处理会受到影响,但在使用内置函数时不会受到影响——这种情况发生得更晚。 (2认同)
  • 这是一个很好的历史总结,但它忽略了上面的 muru 和 John1024 在他们的回答中指出的(IMO)关键细节,即让 `[[` 成为关键字允许 shell 对其参数使用特殊的解析规则。因此,唉,我的赞成票投给了 John1024。 (2认同)