为什么大括号命令组在 POSIX Shell 语法中的左大括号后需要空格?

Ser*_*nyy 12 shell posix subshell syntax

TL;DR:为什么 POSIX 括号组在{保留字后需要空格,而子外壳在保留字后不需要(

POSIX shell 语法定义花括号组和子shell如下

brace_group      : Lbrace compound_list Rbrace

subshell         : '(' compound_list ')'
Run Code Online (Sandbox Code Playgroud)

现在,如果我们从字面上看,空格很重要。这意味着必须有空间描述开始和结束括号和括号,如

{ echo hello world; }

( echo hello world )
Run Code Online (Sandbox Code Playgroud)

这也符合复合命令定义

这些复合命令中的每一个在开头都有一个保留字或控制运算符,并在末尾有一个相应的终止符保留字或运算符。

然而,没有意义的是为什么(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
/*      '{'       '}'       '!'   */
Run Code Online (Sandbox Code Playgroud)

那么,这种差异的确切推理是什么?

接受的答案说明:

  • 移动接受对号Isaac的答案,因为它提供问答uote形成标准直接涉及我的问题本身:

    例如,'(' 和 ')' 是控制运算符,因此<space>(list) 中不需要no 。但是,'{' 和'}' 是{ list;} 中的保留字,因此在这种情况下需要前导<space><semicolon>

  • 接受 Kusalananda 的回答。Kusalananda的回答地址我所需要的,但大多是从的观点非正式直观点; 它指出{是一个保留字,(是运算符。Michael Homer 在评论中也指出了相同的内容 - 复合命令定义指出(强调添加):

    这些复合命令中的每一个在开头都有一个保留字或控制运算符

  • {被定义为保留字,类似于forwhile,在 Shell Grammar 中列出(请参阅问题中的最后一个代码块)

  • 2.9节状态(强调):

    特别是,在一些不需要s 的地方(当标记之一是运算符时),这些表示包括标记之间的间距<blank>

  • 虽然该标准没有明确地定义(为一个运算符,(被称为操作者; 具体而言,部分2.9.2

    如果管道始于保留字!并且 command1 是一个子 shell 命令,应用程序应确保 command1 开头的 ( 运算符与 ! 分隔一个或多个字符。保留字 ! 后跟 ( 运算符的行为未指定。

  • Digital Trauma关于 Stack Overflow问题指出了关于保留字的第 2.4 节:

    仅当没有引用任何字符并且该词用作:

    - 命令的第一个字

  • 正如 Kusalananda 的回答中提到的“POSIX 语法中显示的空格不是 shell 输入数据中需要存在的空格,而只是显示语法本身的一种方式。事实是大括号是保留字,这意味着它们必须被空格包围”正如Michael Homer在评论中提到的那样:“如果空格本身很重要,则需要将它们列在生产中

案件结案。

Kus*_*nda 13

大括号和在括号之间的差是大括号(和!)是保留字,就像forifthen等等而括号是控制操作员。单词需要用空格分隔。

这意味着就像你不能拥有

foriin*; do
Run Code Online (Sandbox Code Playgroud)

你不能有

{somecommand;} >file
Run Code Online (Sandbox Code Playgroud)

或者

if !somecommand; then
Run Code Online (Sandbox Code Playgroud)

POSIX 语法中显示的空格不是 shell 输入数据中需要存在的空格,而只是显示语法本身的一种方式。事实上,大括号是保留,这意味着它们必须被空格包围,而子外壳的括号则不然。


ImH*_*ere 6

这是 shell 将行分解为标记的方式的限制。

shell从输入文件中读取,并根据第 2 节“外壳介绍”将它们转换为单词运算符

  1. shell 将输入分解为标记:单词和运算符

{ 是保留字

有些词是保留字

保留字是对 shell 有特殊意义的字。下列字应被视为保留字:

! { } case do done elif else esac fi for if in then until while
Run Code Online (Sandbox Code Playgroud)

词,要被识别为词,必须被定界

保留字只有在它们被定界时才被识别......

主要由空格(第 7 点)和运算符组成。

  1. 如果当前字符是未加引号的 <blank>,则包含前一个字符的任何标记都将被分隔,并且当前字符将被丢弃。

( 是一个运算符

运营商自立

而操作符本身就是分隔符。

其中“运算符”是

3.260 运算符

在 shell 命令语言中,控制运算符或重定向运算符。

重定向运算符是

重定向运算符

在 shell 命令语言中,执行重定向功能的标记。它是以下符号之一:

<     >     >|     <<     >>     <&     >&     <<-     <>
Run Code Online (Sandbox Code Playgroud)

控制运算符是

3.113 控制操作员

在 shell 命令语言中,执行控制功能的标记。它是以下符号之一:

&   &&   (   )   ;   ;;   newline   |   ||
Run Code Online (Sandbox Code Playgroud)

结论

因此,'(' 和 ')' 是控制运算符,而 '{' '}' 是保留字。

您的问题的完全相同的描述在规范中

例如,'(' 和 ')' 是控制运算符,因此 (list) 中不需要 <space>。然而,'{' 和 '}' 是 { list;} 中的保留字,因此在这种情况下需要前导 <space> 和 <semicolon>。

这正是解释了为什么在{.

这是有效的:

{ echo yes;}
Run Code Online (Sandbox Code Playgroud)

就像这样:

{(echo yes);}
Run Code Online (Sandbox Code Playgroud)

这个:

{(echo yes)}
Run Code Online (Sandbox Code Playgroud)

甚至这个:

{>/dev/tty echo yes;}
Run Code Online (Sandbox Code Playgroud)