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


10

TL; DR:为什么POSIX大括号组在{保留字之后需要空格,但是在保留字之后不需要subshel​​l (

POSIX Shell语法定义了括号组和子shell,如下所示

brace_group      : Lbrace compound_list Rbrace

subshell         : '(' compound_list ')'

现在,如果从字面上看,空格是很重要的。这将意味着必须有空间来描述开括号和闭括号以及括号,如

{ echo hello world; }

( echo hello world )

这也将与“ 复合命令”定义保持一致:

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

但是,没有意义的是为什么(list)并且可以( list )正常工作((不需要后面的空格),但是括号扩展必须有一个前导空格,即{echo hello;}不起作用。

当然,保留字被视为空壳字会有意义,之后需要一个空格以与字段拆分的概念保持一致,但是定义本身并未提及空格。此外,如果POSIX的复合命令定义都将{(都视为保留字,那么为什么在这些保留字之后的空格字符方面会有不同的对待?现在,ksh(1)手册确实指出:

单词是字符序列,由无引号的空格字符(空格,制表符和换行符)或元字符(<,>,|,;,&,(和))定界

换句话说,有意义的是ksh将被识别(为单词定界符,其中第一个单词将是命令或变量赋值。POSIX,但是似乎没有提到(元字符。就POSIX语法而言,我发现的唯一可能的解释是{被认为是“令牌”,此处(未列出为一个。

/* 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的回答。库萨兰南达的答案满足了我的需求,尽管主要是从非正式和直观的角度来看。它指出{是保留字,并且(是运算符。迈克尔·霍默(Michael Homer)在注释中也指出了同样的内容-复合命令定义指出(添加了重点):

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

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

  • 第2.9节规定(强调):

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

  • 虽然该标准未明确定义(为运算符,但(称为运算符;具体来说,第2.9.2节

    如果管道以保留字开头!并且command1是subshel​​l命令,应用程序应确保command1开头的(运算符与!分隔一个或多个字符。保留字!紧跟在(运算符之后)的行为是不确定的。

  • Digital Trauma 关于堆栈溢出问题指出了关于保留字的第2.4节:

    仅当不引用任何字符且该词用作以下内容时,才发生这种识别:

    -命令的第一个字

  • 正如Kusalananda的回答中所述:“ POSIX语法中显示的空格不是shell输入数据中必须存在的空格,而只是显示语法本身的一种方式。事实是,花括号是保留字,这意味着他们已经被空白包围”至于提到的迈克尔·荷马的评论:‘如果空间是在自己的权利显著,他们会需要在上市产品

案件结案。


3
如果这些空间本身具有重要意义,则需要在产品中将其列出。
Michael Homer

2
“此外,复合命令的POSIX定义将if {和if (都视为保留字”cf。“每个复合命令的开头都有保留字或控制运算符 ”。
Michael Homer

2
@SergiyKolodyazhnyy我相信他的意思是,如果空格很重要,则语法中必须包含一个明确的空格字符(' ')。相反,空格由单词标记表示。
库萨兰达

2
至少可以说,令牌类的规范定义很尴尬。整个语法非常糟糕,规范混合了在文本中(有时是隐含的!),语法之前的散文规则以及语法本身中对事物的定义。如果您还不知道答案并倒退,那将是非常不可理解的。词汇规则都是由新的令牌开始的,而不是描述令牌包含的内容,都是反向定义的。到处都是一团糟。
Michael Homer

1
@Sergiy在形式语法中,一个生产(或生产规则)描述了如何从其他内容中生成某些内容。请参见en.wikipedia.org/wiki/Production_%28computer_science%29。这样command : simple_command | compound_command | compound_command redirect_list | function_definition ;的产品说明您可以在何处使用命令,它可以是简单命令,复合命令,带重定向的复合命令或函数定义之一。
大师

Answers:


6

这是外壳将行分割成令牌的方式的限制。

Shell 从输入文件中读取,并根据第2节“ Shell简介”将其转换为单词运算符

  1. 外壳将输入分为令牌:单词和运算符

{是保留字

有些词是保留词

保留字是对shell具有特殊含义的字。以下单词应视为保留字:

! { } case do done elif else esac fi for if in then until while

必须将被识别为单词的单词

保留字仅在分隔时才被识别...

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

  1. 如果当前字符是未加引号的<blank>,则包含前一个字符的所有记号都会被定界,并且当前字符将被丢弃。

(是运算符

操作员自己站着

而运算符本身就是定界符。

其中“经营者”是

3.260操作员

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

重定向运算符是

重定向运算符

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

<     >     >|     <<     >>     <&     >&     <<-     <>

控制运算符是

3.113控制操作员

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

&   &&   (   )   ;   ;;   newline   |   ||

结论

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

规格内包含您的问题完全相同的描述:

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

正是这样解释了为什么在{。之后需要空格(或其他定界符)的原因。

这是有效的:

{ echo yes;}

像这样:

{(echo yes);}

这个:

{(echo yes)}

甚至这个:

{>/dev/tty echo yes;}

好吧,最后的报价正好在!+1了。我现在需要复习问题和答案
Sergiy Kolodyazhnyy

13

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

这意味着就像你不能

foriin*; do

你不能有

{somecommand;} >file

要么

if !somecommand; then

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


1
好吧,这似乎可以回答这个问题,而且我看到它确实说:“特别是,在某些不需要<blank>的地方(当令牌中的一个是运算符时)表示形式包括令牌之间的间距。” 只是一个问题:标准在哪里定义(为运算符?至少它不在语法部分
Sergiy Kolodyazhnyy19年

@MichaelHomer啊,“控制操作员”,就像;。感谢那。
库萨兰达

控制操作符在“定义”下的手册页顶部列出。我们可能会把()控制运算符看作|都涉及子外壳。并且{ }可以在当前Shell中运行,并且不能涉及子Shell。
格伦·杰克曼

@Kusalananda找到了它,在第2.9.2节中:“如果管道以保留字!开头,而command1是子shell命令,则应用程序应确保(command1开头的运算符与!分隔一个或多个<空白>字符。保留字!的行为紧随其后的是(运算符未指定。“没有明确的定义,但是标准确实将其称为(运算符
Sergiy Kolodyazhnyy

@glennjackman虽然流水线确实包含子外壳程序,但这似乎不是合适的定义类型。该标准还提到,在某些实现中,可以在当前的shell执行环境中运行管道(我知道它在标准中,因为我昨天看到了该文本,现在正在寻找它)。但是,您的建议确实使我找到了我在上面评论的报价,至少该标准确实称为运算符,尽管未明确定义为一个运算符
Sergiy Kolodyazhnyy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.