Shell

為什麼大括號命令組在 POSIX Shell 語法中的左大括號後需要空格?

  • June 26, 2019

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;}不起作用。

當然,保留字被視為 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 的答案,因為它提供了標準本身的報價,直接解決了我的問題:

例如,’(’ 和 ‘)’ 是控制運算符,因此&lt;space&gt;(list) 中不需要。但是,’{’ 和 ‘}’ 是 {list;} 中的保留字,因此在這種情況下,前導&lt;space&gt;&lt;semicolon&gt;是必需的。

  • 接受 Kusalananda 的回答。Kusalananda 的回答解決了我的需要,儘管主要是從非正式和直覺的角度來看;它指出{是保留字並且(是運算符。Michael Homer 在評論中也提到了同樣的內容——複合命令定義聲明(強調添加):

這些複合命令中的每一個在開頭都有一個保留字或控制運算符

  • {被定義為保留字,類似於foror while,列在 Shell Grammar 中(參見問題中的最後一個程式碼塊)
  • 第 2.9 節指出(已添加重點):

特別是,這些表示包括在不需要 s的某些地方的標記之間的間距&lt;blank&gt;(當其中一個標記是運算符時)。

  • 雖然標準沒有明確定義(為運算符,但(稱為運算符;具體來說,第 2.9.2 節

如果管道以保留字開頭!並且 command1 是子 shell 命令,應用程序應確保 command1 開頭的 ( 運算符與 ! 分隔一個或多個字元。保留字 ! 的行為緊跟 ( 運算符是未指定的。

只有在沒有引用任何字元並且該詞被用作以下情況時,才會發生這種辨識:

  • 命令的第一個字
  • 正如 Kusalananda 的回答“POSIX 語法中顯示的空格不是 shell 輸入數據中需要存在的空格,而只是顯示語法本身的一種方式。事實上,大括號是保留字,這意味著它們必須被空格包圍”正如邁克爾荷馬在評論中提到的那樣:“如果空格本身很重要,則需要在生產中列出它們”

結案。

這是 shell 將行分成標記的方式的限制。

shell從輸入文件中讀取行,並根據第 2 節“Shell 介紹”將它們轉換為單詞運算符

  1. shell 將輸入分解為標記:單詞和運算符

{ 是保留字

有些字是保留字

保留字是對 shell 具有特殊意義的字。下列字應視為保留字:

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

詞,要被辨識為詞,必須是定界的。

保留字僅在分隔時才被辨識…

主要是通過空格(第 7 點)和操作員。

  1. 如果目前字元是未引用的 <blank>,則包含前一個字元的任何標記都將被分隔,並且目前字元應被丟棄。

( 是一個運算符

運營商自立門戶

而運算符本身就是分隔符。

其中“操作員”是

3.260 運算符

在 shell 命令語言中,可以是控制運算符或重定向運算符。

重定向運算符是

重定向運算符

在 shell 命令語言中,執行重定向功能的標記。它是以下符號之一:

&lt;     &gt;     &gt;|     &lt;&lt;     &gt;&gt;     &lt;&     &gt;&     &lt;&lt;-     &lt;&gt;

控制運算符是

3.113 控制操作員

在 shell 命令語言中,執行控制功能的令牌。它是以下符號之一:

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

結論

因此,’(’ 和 ‘)’ 是控制運算符,而 ‘{’ ‘}’ 是保留字。

您的問題的完全相同的描述在規範中

例如,’(’ 和 ‘)’ 是控制運算符,因此 (list) 中不需要 <space>。但是,’{’ 和 ‘}’ 是 { list;} 中的保留字,因此在這種情況下需要前導 <space> 和 <semicolon>。

這正好解釋了為什麼在{.

這是有效的:

{ echo yes;}

就像這樣:

{(echo yes);}

這:

{(echo yes)}

甚至這樣:

{&gt;/dev/tty echo yes;}

花括號和圓括號的區別在於花括號 (和!) 是保留字,就像for,等一樣ifthen而圓括號是控制運算符。單詞需要用空格分隔。

這意味著就像你不能擁有

foriin*; do

你不能擁有

{somecommand;} &gt;file

或者

if !somecommand; then

POSIX 語法中顯示的空格不是 shell 輸入數據中需要存在的空格,而只是顯示語法本身的一種方式。事實上,大括號是保留,這意味著它們必須被空格包圍,而子shell 的括號則不需要。

引用自:https://unix.stackexchange.com/questions/526574