bashはブレースの展開とコマンドのグループ化をどのように区別しますか?


48

{ブレースの展開に使用できることに気付きました:

echo {1..8}

またはコマンドのグループ化:

{ls;echo hi}

bashはどのように違いを知っていますか?


1
素晴らしい質問、+ 1。{コマンドの先頭に表示される場合はコマンドリストとして解釈され、それ以外の場合はブレース展開として解釈される可能性があるようですが、よくわかりません。
セラダ

16
{ls;echo hi}合法ではありませんbash。開き括弧の後にスペースが必要で、閉じ括弧の前にセミコロンが必要です。
PSkocik

回答:


39

単純化された理由は、1つの文字の存在ですspace

ブレース展開では、スペースは処理されません(引用符で囲まれていません)。

{...}リストは、(非引用された)のスペースを必要とします。

より詳細な答えは、シェルがコマンドラインをどのように解析するかです。


コマンドラインを解析(理解)する最初の手順は、コマンドラインを部分に分割することです。
これらの部分(通常は単語またはトークンと呼ばれます)は、リンクの各メタ文字でコマンドラインを分割した結果です。

  1. コマンドを、メタ文字の固定セット(スペース、タブ、改行、;、(、)、<、>、|、および&)で区切られたトークンに分割します。トークンの種類には、単語、キーワード、I / Oリダイレクタ、セミコロンが含まれます。

メタ文字:spacetabenter;,<>|&

分割後、単語は(シェルで認識されるように)タイプになる場合があります。

  • コマンドの事前割り当て: LC=ALL ...
  • コマンド LC=ALL echo
  • 引数 LC=ALL echo "hello"
  • リダイレクション LC=ALL echo "hello" >&2

ブレースの拡張

「スペース文字またはメタ文字を含まない」「中括弧」が単一の単語であり(上記のとおり)、引用符で囲まれていない場合のみ、「中括弧展開」の候補になります。後で内部構造に対してさらにチェックが実行されます。

したがって、これ{ls,-l}は、「ブレース展開」として修飾されls -lfirst wordまたはのいずれかになりますargument(bashでは、zshは異なります)。

$ {ls,-l}            ### executes `ls -l`
$ echo {ls,-l}       ### prints `ls -l`

しかし、これはそうではありません{ls ,-l}。Bashはspace、2つの単語として行を分割して解析します:{lsそして、,-l}どちらがトリガーされますcommand not found(引数,-l}は失われます):

 $ {ls ,-l}
 bash: {ls: command not found

あなたの行:2つのメタ文字との{ls;echo hi}ため、「ブレース展開」になりません。;space

この3つの部分に分割され{lsますecho hi}。新しいコマンド:。ことを理解し;、新しいコマンドの開始をトリガします。コマンド{lsは検出されず、次のコマンドが出力されますhi}

$ {ls;echo hi}
bash: {ls: command not found
hi}

他のコマンドの後に配置した場合、とにかく新しいコマンドを開始し;ます:

$ echo {ls;echo hi}
{ls
hi}

リスト

「複合コマンド」の1つは「ブレースリスト」(私の言葉)です{ list; }
ご覧のとおり、スペースと終了で定義されています;
スペースとは、;両方のために必要とされている{}、「予約されている言葉」。

したがって、単語として認識されるには、メタ文字で囲まれている必要があります(ほとんどの場合:)space

リンク先ページのポイント2で説明されているように

  1. 各コマンドの最初のトークンをチェックして、それが....、{、または(であるかどうかを確認します。その後、コマンドは実際には複合コマンドです。

あなたの例:{ls;echo hi}リストではありません。

閉じて;、少なくとも1つのスペースが必要{です。最後}は、によって定義され;ます。

これはリスト{ ls;echo hi; }です。これ{ ls;echo hi;}も(あまり一般的ではありませんが、有効です)(@chorobaに感謝します)。

$ { ls;echo hi; }
A-list-of-files
hi

ただし、コマンドの引数(シェルは違いを知っている)として、エラーをトリガーします。

$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'

ただし、シェルが解析していると思われることに注意してください。

$ echo { ls;echo hi;
{ ls
hi

2
これは本当に最高の答えです。bashパーサーの仕組みを教えてください。そして詳細な説明付き!
-lovespring

2
;との間にスペースは必要ありません}{ ls;}セミコロンはすでにメタ文字であるため機能します。
チョロバ

1
@lovespringありがとう、はい、私はそれを書くことにいくらか時間を費やしました。私はそれが有用であることを知ってうれしいです。繰り返しますが、ありがとう。

素晴らしい記事、参考文献に感謝します
エドワード・トーバルズ

16

ブロック{はシェルキーワードであるため、次の単語とスペースで区切る必要がありますが、中括弧の展開ではスペースはありません(スペースを中括弧で展開する必要がある場合は、エスケープする必要がありますecho {\ ,a}{b,c})。

コマンドの開始時にブレース展開を使用できます。

{ls,.}  # expands to "ls ."

ただし、グループ化コマンドの解析は展開前に行われるため、ブロックに展開するために使用することはできません。

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory

5

コマンドラインの構文をチェックすることで認識します。同様に、式echo echoでは、最初のエコーをコマンドとして扱い、2番目のエコーを最初のエコーのパラメーターとして扱う必要があることを知っています。

bashでは、{ cmd; }スペースとセミコロンが必要なので、非常に簡単です。ただし、たとえばzshでは必要ありませんが、それでも{}シェルのコンテキストを分析することで、そのコンテンツで何をすべきかを知ることができます。

以下を考慮してください。

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

どちらも現在の日付を返しますが、

echo {1..3}

戻り1 2 3シェルは知っているので{}、コマンドの引数にはecho、その拡大すべきです。


{引用符で囲まれていないスペースが続くと、bashでブレース展開が開始されません。
チョロバ

@chorobaはい、そして直後だけではありません{。シェルはコマンドライン全体をスペースで分割するため、引用符で囲まれていないスペースはどこにもできません。
-jimmij

0

まず、複合ブレースはそれ自体が単語であり、コマンドラインの最初の単語である必要があります。

echo { these braces are just words }

第二に、個々の中括弧は特別ではありません(上記を参照)。空の中括弧も特別ではありません:

echo {} # just the token {}: familiar from the find command

コンマのないものもそれ自体です

echo {abc} # just {abc}

ここからアクションが始まります。

echo {a,b} # braces disappear, a b results.

したがって、基本的にブレース展開を開始するには、単一の単語(空白でフィールドに分割されていない)が必要{...}です。

これが可能な方法で、コマンドラインの最初の単語こと:

{ls,-l} .   # just "ls -l ."
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.