当()
,我知道括号{}
和花括号grouping commands in bash之间的目的不同。
但是为什么花括号构造在最后一个命令后需要分号,而对于括号构造,分号是可选的?
$ while false; do ( echo "Hello"; echo "Goodbye"; ); done $ while false; do ( echo "Hello"; echo "Goodbye" ); done $ while false; do { echo "Hello"; echo "Goodbye"; }; done $ while false; do { echo "Hello"; echo "Goodbye" }; done bash: syntax error near unexpected token `done' $
我正在寻找一些有关为何情况的见解。我不是在寻找诸如“因为文档说的那样”或“因为它是按照这种方式设计”的答案。我想知道为什么它的设计是这样的。或者,如果它只是一个历史文物?
至少在以下版本的bash中可以观察到这种情况:
因为{
和}
只是命令中的第一个单词才被识别为特殊语法。
这里有两个重点,这两点都可以在bash手册的definitions section中找到。首先,是元字符列表:
metacharacter
一个字符,当不加引号时,分隔单词。元字符是空格或以下字符之一:'|','&',';','(',')','<'或'>'。
该列表包括括号但不包括大括号(既不是卷曲也不是方形)。请注意,它不是具有特殊含义的字符的完整列表,但它是一个完整的字符列表,用于分隔标记。所以{
和}
不会分开令牌,只有当它们与元字符(例如空格或分号)相邻时才会被认为是令牌。
虽然大括号不是元字符,但它们由parameter expansion(例如${foo}
)和brace expansion(例如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中非常明确(从上面列出了保留字的列表,除了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。并且不要试图理解它:它只是一个随机的任意选择和历史异常的集合,很多但不是全部基于不用新功能打破古代shell脚本的需要。