我知道在bash中对命令进行分组时,括号()
和大括号{}
之间的用途不同。
但是,为什么大括号结构需要在最后一个命令后使用分号,而对于圆括号结构,分号是可选的呢?
$while false;do(echo"你好";echo"再见";);已完成$while false;do(echo"你好";echo"再见");已完成$while false;do{echo"你好";echo"再见";};已完成$while false;do{echo"Hello";echo"Goodbye"};已完成bash:意外标记"done"附近出现语法错误$
我想了解为什么会出现这种情况。我不是在寻找之类的答案,因为文档上写着">或",因为它是这样设计的。我想知道为什么它是这样设计的。或者,如果它只是一件历史文物?
这至少可以在以下版本的bash中观察到:
- GNU bash,版本3.00.15(1)-发行版(x86_64-redhat-linux-GNU)
- GNU bash,版本3.2.48(1)-发行版(x86_64-apple-darwin12)
- GNU bash,版本4.2.25(1)-发布(x86_64-pc-linux-GNU)
因为只有当{
和}
是命令中的第一个单词时,它们才会被识别为特殊语法。
这里有两个要点,这两个要点都可以在bash手册的定义部分找到。首先,是元字符列表:
metacharacter
一个字符,当不加引号时,它将单词分隔开。元字符是空白字符或以下字符之一:"|"、"&"、";"、(',')','<',或">"。
该列表包括圆括号,但不包括大括号(既不是大括号也不是方形括号)。请注意,这不是一个对shell具有特殊意义的完整字符列表,但它是一个完整的字符列表,将标记分隔开。因此,
{
和}
不分离标记,只有当它们与元字符(如空格或分号)相邻时,才会被视为标记本身。虽然大括号不是元字符,但在参数扩展(例如
${foo}
)和大括号扩展(例如,foo.{c,h}
)中,shell会对它们进行特殊处理。除此之外,他们只是普通的角色。例如,将文件命名为{ab}
或}{
没有问题,因为这些单词不符合参数展开(需要在{
之前有一个$
)或大括号展开(需要{
和}
之间至少有一个逗号)的语法。因此,您可以使用{
或}
作为文件名,而不必引用符号。类似地,您可以调用文件if
、done
或time
,而不必考虑引用名称。后一种令牌是";保留字":
reserved word
一个对shell有特殊含义的单词。大多数保留字引入shell流控制结构,如
for
和while
。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中非常明确(从Posix中提取了上述保留字列表,
time
除外):只有当没有引用任何字符并且该单词被用作:时,才能识别[作为保留单词]
- 命令的第一个单词
(识别保留字的位置的完整列表略长,但以上是一个很好的总结。)换句话说,只有当保留字是命令的第一个字时,保留字才会被保留。而且,由于
{
和}
是保留字,因此只有当它们是命令中的第一个字时,它们才是特殊语法。示例:
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。不要试图理解它:它只是任意选择和历史异常的随机集合,其中许多但并非全部基于不需要用新功能打破古老的外壳脚本。