bash命令组:为什么大括号需要分号?
我知道在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手册的定义部分找到。 首先是元字符列表:
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中非常明确(除了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脚本的需要。
上一篇: bash command groups: Why do curly braces require a semicolon?
下一篇: How can I escape curly braces at the end of a regular expression