25

在 bash 中对命令进行分组时,我知道括号()和花括号之间的用途差异。{}

但是为什么花括号结构在最后一个命令之后需要一个分号,而对于括号结构,分号是可选的?

$ 而假;做(回声“你好”;回声“再见”;);完毕
$ 而假;做(回声“你好”;回声“再见”);完毕
$ 而假;做{回声“你好”;回声“再见”;}; 完毕
$ 而假;做{回声“你好”;回声“再见”}; 完毕
bash:意外标记“完成”附近的语法错误
$

我正在寻找一些关于为什么会这样的见解。我不是在寻找诸如“因为文档是这样说的”“因为它是这样设计的”之类的答案。我想知道为什么它是这样设计的。或者,如果它只是一个历史文物?

这至少可以在以下版本的中观察到:

  • GNU bash,版本 3.00.15(1)-release (x86_64-redhat-linux-gnu)
  • GNU bash,版本 3.2.48(1)-release (x86_64-apple-darwin12)
  • GNU bash,版本 4.2.25(1)-release (x86_64-pc-linux-gnu)
4

1 回答 1

35

因为{and}仅当它们是命令中的第一个单词时才被识别为特殊语法。


这里有两点很重要,都可以在bash 手册的定义部分找到。首先,是元字符列表:

metacharacter

一个字符,当不加引号时,分隔单词。元字符是空格或以下字符之一:'|'、'&'、';'、'('、')'、'<' 或 '>'。

该列表包括括号,但不包括大括号(既不是花括号也不是方括号)。请注意,它不是对 shell 具有特殊含义的字符的完整列表,而是分隔标记的字符的完整列表。因此{}不要分隔标记,并且仅当它们与元字符(例如空格或分号)相邻时才被视为标记本身。

尽管大括号不是元字符,但它们在参数扩展(例如。${foo})和大括号扩展(例如。foo.{c,h})中被 shell 特殊处理。除此之外,他们只是普通的角色。命名文件没有问题{ab},例如 or }{,因为这些词不符合参数扩展(需要在$之前有a )或大括号扩展(在and{之间至少需要一个逗号)的语法。就此而言,您可以使用or作为文件名,而不必引用符号。同样,您可以调用文件,或者无需考虑引用名称。{}{}ifdonetime

后面的这些标记是“保留词”:

reserved word

一个对 shell 有特殊含义的词。大多数保留字都会引入 shell 流控制结构,例如forwhile

bash 手册不包含保留字的完整列表,这是不幸的,但它们肯定包括 Posix 指定的:

!    {    }
case do   done elif else
esac fi   for  if   in
then until while

以及由 bash (和其他一些 shell)实现的扩展:

[[   ]]
function  select time

这些词与内置词(例如[)不同,因为它们实际上是 shell 语法的一部分。内置函数可以实现为函数或 shell 脚本,但保留字不能,因为它们改变了 shell 解析命令行的方式。

保留字有一个非常重要的特性,它实际上并没有在 bash 手册中突出显示,但在Posix中非常明确(上面的保留字列表是从中获取的,除了time):

这种识别[作为保留字]仅在没有引用任何字符并且该词用作以下情况时才会发生:

  • 命令的第一个字……</li>

(识别保留字的位置的完整列表稍长,但上面是一个很好的总结。)换句话说,保留字仅在它们是命令的第一个字时才被保留。并且,由于{}是保留字,它们只有在命令中的第一个字是特殊语法时才属于特殊语法。

例子:

ls }  # } is not a reserved word. It is an argument to `ls`
ls;}  # } is a reserved word; `ls` has no arguments

关于 shell 解析,尤其是 bash 解析,我还有很多可以写的,但很快就会变得乏味。(例如,关于何时#开始评论以及何时只是一个普通字符的规则。)大致的总结是:“不要在家里尝试这个”;实际上,唯一可以解析 shell 命令的就是 shell。并且不要试图理解它:它只是任意选择和历史异常的随机集合,其中许多但并非全部基于不破坏具有新功能的古老 shell 脚本的需要。

于 2014-01-20T23:56:57.283 回答