TL; DR:POSIXブレースグループが{
予約語の後にスペースを必要とするのに、サブシェルは予約語(
の後に必要ないのはなぜですか?
POSIXシェルの文法は、ブレースグループとサブシェルを次のように定義します
brace_group : Lbrace compound_list Rbrace
subshell : '(' compound_list ')'
今、それを文字通り読んでいる場合、スペースは重要です。これは、次のように、中括弧と括弧を開始と終了で区切るスペースが必要であることを意味します。
{ echo hello world; }
( echo hello world )
これは、 複合コマンドの定義 とも一致します。
これらの複合コマンドのそれぞれには、最初に予約語または制御演算子があり、最後に対応するターミネーター予約語または演算子があります。
ただし、意味がないのは、(list)
および( list )
が正常に機能する理由です((
の後のスペースは必要ありません)。ただし、中括弧の拡張には先行スペース、つまり{echo hello;}
は機能しません。
もちろん、予約語がシェルワードとして扱われると、 フィールド分割 の概念に合わせるために後でスペースが必要になりますが、定義自体はスペースについて言及していません。さらに、{
と(
の両方が複合コマンドのPOSIX定義で予約語と見なされている場合、これらの予約語の後の空白文字に関してそれらが異なる扱いを受けるのはなぜですか?現在、 ksh(1) マニュアルは次のように述べています:
文字のシーケンスである単語は、引用符で囲まれていない空白文字(スペース、タブ、改行)またはメタ文字(<、>、|、;、&、(および))で区切られます。
言い換えると、kshが(
をWordの区切り文字として認識することは理にかなっています。最初のWordはコマンドまたは変数の割り当てです。ただし、POSIXは(
をメタ文字として言及していないようです。 POSIX文法に関して私が見つけた唯一の可能な説明は、{
は「トークン」と見なされ、(
は1としてリストされていないということです。
/* These are reserved words, not operator tokens, and are
recognized when reserved words are recognized. */
%token Lbrace Rbrace Bang
/* '{' '}' '!' */
それで、この矛盾の正確な理由は何でしょうか?
受け入れられたチェックマークを Isaacの回答に移動しました それは標準からq uoteを提供するため それ自体が私の質問に直接対処します:
たとえば、 '('および ')'は制御演算子であるため、(リスト)に
<space>
は必要ありません。ただし、 '{'および '}'は{list;}の予約語であるため、この場合は先頭の<space>
および<semicolon>
が必要です。
クサラナンダの答えを受け入れる。クサラナンダの答えは、ほとんどが非公式で直感的な見地からですが、私が必要とするものを扱います。 {
は予約語であり、(
は演算子です。 Michael Homerもコメントで同じことを指摘しました-複合コマンドの定義は次のように述べています(強調は追加されています):
これらの複合コマンドのそれぞれには、最初に予約語または制御演算子があります
{
はfor
またはwhile
と同様に予約語として定義され、シェル文法にリストされています(質問の最後のコードブロックを参照)
セクション2.9の状態(強調を追加):
特に、表現には、
<blank>
sが不要な場所でトークン間のスペースが含まれます(トークンの1つが演算子の場合)。
標準では明示的に(
を演算子として定義していませんが、(
は演算子と呼ばれています。具体的には、 セクション2.9.2 は
パイプラインが予約語で始まる場合!およびcommand1がサブシェルコマンドである場合、アプリケーションは、command1の先頭の(演算子が!から1つ以上の文字で区切られていることを確認する必要があります。予約語!の直後に(演算子が続くことにより、.
スタックオーバーフローに関する質問 Digital Traumaによる予約語に関するセクション2.4の指摘:
この認識は、どの文字も引用されておらず、Wordが次のように使用されている場合にのみ発生します。
-コマンドの最初の単語
Kusalanandaの回答で述べたように、「POSIX文法に示されるスペースは、シェルの入力データに必要なスペースではなく、文法自体を表示する方法にすぎません。中かっこは予約語であり、 Michael Homer がコメントで述べているように、「スペースがそれ自体で重要である場合は、 プロダクション にリストする必要があります。」
ケースは閉じられました。
これは、シェルが行をトークンに分割する方法の制限です。
シェルは入力ファイルからlinesを読み取り、 セクション2の「シェルの概要」による を次のいずれかに変換しますWordまたは演算子:
- シェルは入力をトークンに分解します:単語と演算子
予約語は、シェルにとって特別な意味を持つ語です。次の単語は予約語として認識されます。
! { } case do done Elif else esac fi for if in then until while
単語、単語として認識される で区切られている必要があります 。
予約語は、区切られている場合にのみ認識されます...
ほとんどの場合 空白(ポイント7)による および演算子による。
- 現在の文字が引用符で囲まれていない<空白>の場合、前の文字を含むトークンは区切られ、現在の文字は破棄されます。
一方、演算子自体は区切り文字です。
3.260オペレーター
シェルコマンド言語では、control演算子またはredirection演算子のいずれか。
リダイレクト演算子
シェルコマンド言語で、リダイレクト機能を実行するトークン。次の記号のいずれかです。
< > >| << >> <& >& <<- <>
制御演算子は :
3.113制御オペレーター
シェルコマンド言語で、制御機能を実行するトークン。次の記号のいずれかです。
& && ( ) ; ;; newline | ||
したがって、「(」と「)」は制御演算子であり、「{」「}」は予約語です。
そして、まったく同じ あなたの質問は仕様の中にあります :
たとえば、「(」と「)」は制御演算子なので、(リスト)に<space>は必要ありません。ただし、 '{'および '}'は{list;}の予約語であるため、この場合は先頭の<space>および<semicolon>が必要です。
これは、{
の後にスペース(または他の区切り文字)が必要な理由を正確に説明しています。
これは有効です:
{ echo yes;}
これは:
{(echo yes);}
この:
{(echo yes)}
またはこれでも:
{>/dev/tty echo yes;}
中括弧と括弧の違いは、中括弧(および!
)は予約語であり、for
、if
、then
などと同様に、括弧は制御演算子です。単語は空白で区切る必要があります。
これは、あなたが持つことができないのと同じように
foriin*; do
持てない
{somecommand;} >file
または
if !somecommand; then
POSIX文法に示されているスペースは、シェルの入力データに存在する必要があるスペースではなく、文法自体を表示する方法にすぎません。中かっこが予約されているという事実ですwords空白で囲む必要があることを意味しますが、サブシェルの括弧はそうではありません。