为什么 `[` 是 shell 内置函数,而 `[[` 是 shell 关键字?

为什么 `[` 是 shell 内置函数,而 `[[` 是 shell 关键字?

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

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

TLDP

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

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

这不应该使[and[[成为关键字吗?我在这里缺少什么吗?还,这个链接再次确认[[[应该属于同一类。

答案1

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

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

    [ -z $name ]
    

    shell 将展开$name并执行两个都对结果进行分词和文件名生成,就像任何其他命令一样。

    例如,以下操作将失败:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments
    

    为了使其正常工作,需要引号:

    $ [ -n "$name" ] && echo not empty
    not empty
    
  • [[是一个 shell 关键字,其参数根据特殊规则进行处理。例如,考虑:

    [[ -z $name ]]
    

    shell 将展开$name,但与任何其他命令不同,它将执行两者都不对结果进行分词或文件名生成。例如,尽管 中嵌入了空格,以下内容仍将成功name

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty
    

概括

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

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

答案2

V7 Unix— Bourne shell 首次亮相的地方 —[被称为test,并且它仅以/bin/test.因此,您今天将编写的代码如下:

if [ "$foo" = "bar" ] ; then ...

你会写成

if test "$foo" = "bar" ; then ...

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

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

[并且test可以使用相同的代码来实现。 OS X/bin/[就是这种情况/bin/test,其中这些是硬链接到同一个可执行文件。因此,该实现完全忽略了尾随]:如果您将其称为 as ,则它不需要它/bin/[,并且如果您将其称为提供给/bin/test.⁴

这些历史都不会产生影响[[,因为从来没有一个名为 的原始程序[[。它纯粹存在于那些将其实现为扩展的 shell 中POSIX外壳

“内置”和“关键字”之间的部分区别是由于这段历史。这也反映了解析表达式的语法规则[[不同的事实,如中指出的约翰1024的回答.⁵


脚注:

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

  2. 它发生在 1980 年左右。/bin/[在我的副本中不存在古老的 Unix V7从 1979 年开始,也没有man test将其记录为别名。在相应的手册页条目中,我有一个预发行版本系统三1980年的手册,它列出。

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

  4. 但不要指望这种行为。 Bash 内置版本[确实需要关闭],并且如果您这样做,它的内置test实现将会抱怨提供它。

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

答案3

[最初只是一个外部命令,/bin/test.但是有一些命令(例如[echo)在 shell 脚本中使用得非常频繁,以至于 shell 实现者决定将代码直接复制到 shell 本身中,而不是每次使用它们时都必须运行另一个进程。这将这些命令变成了“内置命令”,尽管您仍然可以通过其完整路径调用外部程序。

[[来得很晚。尽管内置命令是在 shell 内部实现的,但它的解析方式与外部命令一样。正如 John1024 的回答中所解释的,这意味着未加引号的变量将对其进行分词,并且像>和这样的标记<将作为 I/O 重定向进行处理。这使得编写复杂的比较表达式很不方便。[[被创建为 shell 语法,因此可以对其进行特殊解析。变量内部[[不会进行分词,<并且>可以用作比较运算符,=根据下一个参数是否被引用而表现不同,等等。这些都是比传统命令/内置命令[[更容易使用的便利。[

他们不能简单地重新编码[为这样的语法,因为这对于数百万个脚本来说是不兼容的更改。通过使用[[以前不存在的新语法,他们可以以向上兼容的方式完全改变其使用方式。

这类似于$((...))算术表达式语法的演变,它基本上取代了传统expr命令。

答案4

较新[[bash优化[

经典[的有一个很大的缺点,当它经常用于执行一项琐碎的操作时:它每次都会产生一个新进程:(
它创建一个新的地址空间只是为了比较01!每次!)

我认为添加的一个要点[[是使内部表达式的求值[不会产生额外的过程。但[工作方式无法改变——这会造成很多混乱和问题。因此,优化以一个更有效的新名称来实现,即 shell 内置命令。
作为副作用,它成为 shell 语法中的关键字。

当时[最先使用的是,这是正确的方式使用外部进程来完成此操作。

相关内容