TL;DR:为什么 POSIX 大括号组在{
保留字后需要空格,而 subshell 在保留字后不需要空格(
?
POSIX shell 语法定义大括号组和子shell 如下
brace_group : Lbrace compound_list Rbrace
subshell : '(' compound_list ')'
现在,如果我们从字面上理解,空格很重要。这意味着必须有空格来描述左大括号和圆括号,如
{ echo hello world; }
( echo hello world )
这也将与复合命令定义保持一致:
这些复合命令中的每一个在开头都有一个保留字或控制运算符,在末尾有一个相应的终止符保留字或运算符。
然而,没有意义的是为什么(list)
并且( list )
工作得很好((
不需要后面的空格),但是大括号扩展必须有一个前导空格,即{echo hello;}
不起作用。
当然,保留字被视为 shell 字之后需要一个空格以与字段拆分的概念保持一致是有意义的,但是定义本身并没有提及空格。此外,如果{
和(
都被复合命令的 POSIX 定义视为保留字,为什么在这些保留字之后的空格字符方面对它们进行不同的处理?现在,ksh(1)手册确实说明了:
单词是字符序列,由不带引号的空白字符(空格、制表符和换行符)或元字符(<、>、|、;、&、(和))分隔
换句话说,ksh 将识别(
为单词分隔符是有意义的,其中第一个单词将是命令或变量赋值。然而,POSIX 似乎没有提到(
元字符。就 POSIX 语法而言,我发现的唯一可能的解释是它{
被认为是一个“令牌”,其中 as(
没有被列为一个。
/* These are reserved words, not operator tokens, and are
recognized when reserved words are recognized. */
%token Lbrace Rbrace Bang
/* '{' '}' '!' */
那么这种差异的确切原因是什么?
接受的答案说明:
将接受的复选标记移至Isaac 的答案,因为它提供了标准本身的报价,直接解决了我的问题:
例如,'(' 和 ')' 是控制运算符,因此
<space>
(list) 中不需要。但是,'{' 和 '}' 是 {list;} 中的保留字,因此在这种情况下,前导<space>
和<semicolon>
是必需的。接受 Kusalananda 的回答。Kusalananda 的回答解决了我的需要,尽管主要是从非正式和直观的角度来看;它指出{
是保留字并且(
是运算符。Michael Homer 在评论中也提到了同样的内容——复合命令定义声明(强调添加):这些复合命令中的每一个在开头都有一个保留字或控制运算符
{
被定义为保留字,类似于for
orwhile
,列在 Shell Grammar 中(参见问题中的最后一个代码块)第 2.9 节指出(已添加重点):
特别是,这些表示包括在不需要 s的某些地方的标记之间的间距
<blank>
(当其中一个标记是运算符时)。虽然标准没有明确定义
(
为运算符,但(
称为运算符;具体来说,第 2.9.2 节说如果管道以保留字开头!并且 command1 是子 shell 命令,应用程序应确保 command1 开头的 ( 运算符与 ! 分隔一个或多个字符。保留字 ! 的行为紧跟 ( 运算符是未指定的。
Digital Trauma关于 Stack Overflow的问题指出了关于保留字的第 2.4 节:
只有在没有引用任何字符并且该词被用作以下情况时,才会发生这种识别:
- 命令的第一个字
正如 Kusalananda 的回答“POSIX 语法中显示的空格不是 shell 输入数据中需要存在的空格,而只是显示语法本身的一种方式。事实上,大括号是保留字,这意味着它们必须被空格包围”正如迈克尔荷马在评论中提到的那样:“如果空格本身很重要,则需要在生产中列出它们”
结案。
花括号和圆括号的区别在于花括号 (和
!
) 是保留字,就像for
,等一样if
,then
而圆括号是控制运算符。单词需要用空格分隔。这意味着就像你不能拥有
你不能拥有
或者
POSIX 语法中显示的空格不是 shell 输入数据中需要存在的空格,而只是显示语法本身的一种方式。事实上,大括号是保留字,这意味着它们必须被空格包围,而子shell 的括号则不需要。
这是 shell 将行分成标记的方式的限制。
shell从输入文件中读取行,并根据第 2 节“Shell 介绍”将它们转换为单词或运算符:
{ 是保留字
有些字是保留字
词,要被识别为词,必须是定界的。
主要是通过空格(第 7 点)和操作员。
( 是一个运算符
运营商自立门户:
其中“操作员”是:
重定向运算符是:
控制运算符是:
结论
因此,'(' 和 ')' 是控制运算符,而 '{' '}' 是保留字。
您的问题的完全相同的描述在规范中:
这正好解释了为什么在
{
.这是有效的:
就像这样:
这个:
甚至这样: