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
