なぜPOSIX Shell Grammarの開始中括弧の後に中括弧コマンドグループにスペースが必要なのですか?


10

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が(最初の単語がコマンドまたは変数の割り当てである単語の区切り文字として認識することは理にかなっています。ただし、POSIXは(メタ文字として言及されていないようです。POSIX文法に関して私が見つけた唯一の可能な説明{は、「トークン」と見なさ(れるというものです。

/* These are reserved words, not operator tokens, and are
   recognized when reserved words are recognized. */


%token  Lbrace    Rbrace    Bang
/*      '{'       '}'       '!'   */

では、この矛盾の正確な理由は何でしょうか?

受け入れられた回答メモ:

  • 受け入れられたチェックマークをアイザックの回答に移動しました。これは、私の質問に直接対処する標準自体からの引用を提供するためです。

    たとえば、「(」と「)」は制御演算子であるため<space>、(リスト)では不要です。ただし、「{」と「}」は{list;}の予約語であるため、この場合は先頭に<space><semicolon>が必要です。

  • クサラナンダの答えを受け入れる。クサラナンダの答えは私が必要とするものを扱っていますが、ほとんどは非公式で直感的な観点からです。{予約語で(あり、演算子であると指摘しています。Michael Homerもコメントで同じことを指摘しました-複合コマンドの定義は次のように述べています(強調を追加)

    これらの複合コマンドのそれぞれには、最初に予約語または制御演算子があります

  • {シェル文法にリストされている、forまたはに類似した予約語として定義されているwhile(質問の最後のコードブロックを参照)

  • セクション2.9の状態(強調を追加):

    特に、表現にはsが不要な場所(トークンの1つが演算子の場合)でのトークン間のスペースが含ま<blank>ます。

  • 標準では(演算子として明示的に定義されていませんが、演算子(と呼ばれます。具体的には、セクション2.9.2は言う

    パイプラインが予約語で始まる場合!また、command1がサブシェルコマンドである場合、アプリケーションは、command1の先頭の(演算子が!から1つ以上の文字で区切られていることを確認する必要があります。予約語!の直後に(演算子が続くことにより、

  • Digital Traumaによるスタックオーバーフローに関する質問は、予約語に関するセクション2.4を指摘しています。

    この認識は、文字が引用されておらず、単語が次のように使用されている場合にのみ発生します。

    -コマンドの最初の単語

  • Kusalanandaの回答で述べたように、「POSIX文法で示されるスペースは、シェル入力データに存在する必要があるスペースではなく、文法自体を表示する方法にすぎません。中括弧は予約語であり、 「で述べたように、彼らは空白に囲まれているマイケル・ホーマーコメントで: 『スペースは、それ自体で有意であったならば、彼らはにリストする必要があると思いプロダクション

ケースは閉じられました。


3
スペースがそれ自体で重要である場合は、プロダクションにリストする必要があります。
Michael Homer

2
「また、場合{(の両方の化合物コマンドのPOSIX定義によって予約語であると考えられる」参照 「これらの複合コマンドのそれぞれには、最初に予約語または制御演算子があります。」
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 ;は、コマンドを作成できる場所を示すプロダクションであり、単純なコマンド、複合コマンド、またはリダイレクトを伴う複合コマンド、または関数定義のいずれかです。
muru

回答:


6

これは、シェルが行をトークンに分割する方法の制限です。

シェルは入力ファイルからを読み取り、セクション2 "Shell Introduction"に従って、それらを単語または演算子に変換します。

  1. シェルは入力をトークンに分解します:単語と演算子

{は予約語です

一部の単語は予約語です

予約語は、シェルにとって特別な意味を持つ語です。次の単語は予約語として認識されます。

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

単語として認識されるには、単語を区切る必要があります

予約語は、区切られている場合にのみ認識されます...

に空白(ポイント7)と演算子によって。

  1. 現在の文字が引用符で囲まれていない<空白>の場合、前の文字を含むトークンは区切られ、現在の文字は破棄されます。

(は演算子です

オペレーターは自立する

一方、演算子自体は区切り文字です。

どこで「事業者」はどちらかであります

3.260オペレーター

シェルコマンド言語では、制御演算子またはリダイレクト演算子。

リダイレクト演算子は次のとおりです。

リダイレクト演算子

シェルコマンド言語で、リダイレクト機能を実行するトークン。次のいずれかの記号です。

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

制御演算子は次のとおりです。

3.113制御オペレーター

シェルコマンド言語で、制御機能を実行するトークン。次のいずれかの記号です。

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

結論

したがって、「(」と「)」は制御演算子であり、「{」「}」は予約語です。

そして、あなたの質問とまったく同じ説明が仕様の中にあります

たとえば、「(」と「)」は制御演算子であるため、(リスト)に<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文法に示されているスペースは、シェル入力データに存在する必要があるスペースではなく、文法自体を表示する方法にすぎません。中括弧は予約であるため、空白で囲む必要があることを意味しますが、サブシェルの括弧はそうではありません。


1
ええと、これはほとんど答えているようで、「特に、<トークン>が演算子である場合、<blank>が不要な場所ではトークン間の間隔が表現に含まれています」と表示されているのがわかります。たった一つの質問:標準は(演算子としてどこを定義していますか?少なくとも文法セクションにはありません
セルギーコロディアズニー

@MichaelHomerああ、「コントロールオペレーター」と同じです;。それをありがとう。
クサラナンダ

制御演算子は、マニュアルページの上部のDEFINITIONSにリストされています。どちらもサブシェルを含むという点で()、制御演算子と|見なすことができます。また{ }、現在のシェルで動作し、サブシェルを含めることはできません。
グレン・ジャックマン

@Kusalanandaが見つけた、セクション2.9.2:「パイプラインが予約語!で始まり、command1がサブシェルコマンドである場合、アプリケーションは、command1の先頭の(演算子が!から1つ以上< blank>文字。予約語!の直後に(演算子が続く動作は指定されていません。 "明確な定義ではありませんが、標準ではそれを(演算子と呼んでいます
Sergiy Kolodyazhnyy

@glennjackmanパイプラインがサブシェルを含むことは事実ですが、それは適切と思われる定義のタイプではありません。また、一部の実装では、パイプラインを現在のシェル実行環境で実行しても問題がないことも記載されています(昨日テキストを見て、今探しているので、標準に含まれていることがわかります)。しかし、あなたの提案は私が上記でコメントした引用を見つけることを私にポイントしました、そこで、少なくとも標準はそれを明示的にそれを演算子として定義しませんが、それを演算子と呼びます
Sergiy Kolodyazhnyy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.