web-dev-qa-db-ja.com

バッシュのネストされたブレース拡張の謎

この:

$ echo {{a..c},{1..3}}

これを生成します:

a b c 1 2 3

これはいいですが、それを考えると説明するのは難しいです

$ echo {a..c},{1..3}

与える

a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3

これはどこかに文書化されていますか? Bash Reference はそれを言及していません(たとえそれを使用する例があるとしても)。

19
xenoid

これが簡単な答えです。最初の式では、コンマが区切り文字として使用されているため、中括弧の展開は、2つのネストされた部分式の連結にすぎません。 2番目の式では、コンマ自体が1文字の部分式として扱われるため、積式が形成されます

欠けていたのは、ブレース展開がどのように実行されるかの定義でした。 3つの参照があります。

より詳細な説明を以下に示します。


この式の結果を比較しました。

$ echo {{a..c},{1..3}}
a b c 1 2 3

この式の結果:

$ echo {a..c},{1..3}
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3

これは説明するのが難しい、つまり直感に反していると言います。不足しているのは、ブレース展開の処理方法の正式な定義です。 Bash Manual は完全な定義を提供しないことに注意してください。

少し検索しましたが、不足している(完全な、正式な)定義も見つかりませんでした。だから私はソースコードに行きました:

ソースには、役立つコメントがいくつか含まれています。まず、ブレース拡張アルゴリズムの概要です。

Basic idea:

Segregate the text into 3 sections: preamble (stuff before an open brace),
postamble (stuff after the matching close brace) and amble (stuff after
preamble, and before postamble).  Expand amble, and then tack on the
expansions to preamble.  Expand postamble, and tack on the expansions to
the result so far.

したがって、中かっこ展開トークンの形式は次のとおりです。

<PREAMBLE><AMBLE><POSTAMBLE>

展開の主なエントリポイントは、次のように説明されるbrace_expandという関数です。

Return an array of strings; the brace expansion of TEXT.

したがって、brace_expand関数は、ブレース展開式を表す文字列を取り、展開された文字列の配列を返します。

これら2つの観察結果を組み合わせると、アンブルが文字列のリストに拡張され、それぞれがプリアンブルに連結されていることがわかります。次に、ポストアンブルが文字列のリストに展開され、ポストアンブルリストの各文字列がプリアンブル/アンブルリストの各文字列に連結されます(つまり、2つのリストの積が形成されます)。しかし、これはアンブルとポストアンブルの処理方法については説明していません。幸いにも、それを説明するコメントがあります。アンブルは、expand_ambleという関数によって処理されます。この関数の定義の前には、次のコメントがあります。

Expand the text found inside of braces.  We simply try to split the
text at BRACE_ARG_SEPARATORs into separate strings.  We then brace
expand each slot which needs it, until there are no more slots which
need it.

コードの他の場所では、BRACE_ARG_SEPARATORがコンマとして定義されていることがわかります。これにより、アンブルがコンマで区切られた文字列のリストであることが明確になり、その一部はブレース展開式である場合もあります。これらの文字列は、単一の配列を形成します。最後に、expand_ambleが呼び出された後、brace_expand関数がポストアンブルで再帰的に呼び出されることもわかります。これにより、アルゴリズムの完全な説明が得られます。

この発見を裏付けるいくつかの他の(非公式の)参照があります。

1つのリファレンスとして、 Bash Hackers Wiki を確認してください。 結合とネスト のセクションでは問題を完全に扱っていませんが、ページは中括弧の展開の構文/文法を提供しています。構文は、次のパターンで提供されます。

{string1,string2,...,stringN}

{<START>..<END>}

<PREAMBLE>{........}

{........}<POSTSCRIPT>

<PREAMBLE>{........}<POSTSCRIPT>

そして、解析は次のように記述されます:

ブレース展開は、任意の文字列を生成するために使用されます。指定された文字列は可能なすべての組み合わせを生成するために使用され、オプションで周囲のプリアンブルとポストスクリプトを使用します。

別のリファレンスについては、 Bash初心者向けガイド を参照してください。

Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace-expanded take the form of an optional PREAMBLE, followed by a series of comma-separated strings between a pair of braces, followed by an optional POSTSCRIPT. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.

したがって、中括弧展開式を解析するために、左から右に進み、各式を展開し、(文字列連結の操作に関して)連続する積を形成します。

次に、最初の式について考えてみましょう。

{{a..c},{1..3}}

Bash Hacker's Wikiの言語では、これは最初の形式と一致します。

{string1,string2,...,stringN}

ここで、N=2string1={a..c}およびstring2={1..3}-最初に中括弧の展開が実行され、それぞれが{<START>..<END>}の形式になります。あるいは、これはアンブルのみで構成される(プリアンブルまたはポストアンブルではない)ブレース展開式であると言えます。アンブルはコンマ区切りのリストなので、リストを一度に1スロットずつ調べ、必要に応じて追加の展開を実行します。隣接する式がないため、製品は形成されません(コンマが区切り記号として使用されます)。

次に、2番目の式を見てみましょう。

{a..c},{1..3}

Bash HackerのWikiの言語では、この表現は次の形式に一致します。

{........}<POSTSCRIPT>

ここで、ポストスクリプトは部分式,{1..3}です。あるいは、この式にはアンブル({a..c})とポストアンブル(,{1..3})があると言えます。アンブルはリストa b cに展開され、ポストアンブルの展開でこれらのそれぞれが各文字列と連結されます。ポストアンブルは再帰的に処理されます。プリアンブルは,で、アンブルは{1..3}です。リスト,1 ,2 ,3に展開されます。次に、2つのリストa b c,1 ,2 ,3を組み合わせて、製品リストa,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3を作成します。

これらの式がどのように解析されるかを疑似代数で説明すると、大括弧 "[]"が配列を示し、 "+"が配列の連結を示し、 "*"がデカルト積(連結に関して)を示します。

最初の式を展開する方法を次に示します(1行につき1ステップ)。

{{a..c},{1..3}}
{a..c} + {1..3}
[a b c] + [1 2 3]
a b c 1 2 3

次に、2番目の式がどのように展開されるかを示します。

{a..c},{1..3}
{a..c} * ,{1..3}
[a b c] * [,1 ,2 ,3]
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
7
igal

私の理解はこれです:

内側のブレースが最初に解決され(いつものように)回転します

{{a..c},{1..3}}

{a,b,c,1,2,3}

なぜなら ,はブレース内にあり、ブレース要素を分離するだけです。

しかしの場合

{a..c},{1..3}

,は中括弧内にありません。つまり、これは通常の文字であり、両側で中括弧の置換を引き起こします。

2
Hauke Laging