Bashの入れ子ブレース拡張ミステリー


19

この:

$ 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

これはどこかに文書化されていますか?バッシュリファレンス(それはそれを使用した例であっても)それを言及しません。

回答:


18

さて、それは一度に1つのレイヤーが解かれています:

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

展開されているものとして文書化されているX{a..c}Y X{1..3}Y(のがあることX{A,B}Yに拡大XA XBしてAいる{a..c}Bされ{1..3})、自身がに展開されているものとして文書化XaY XbY XcY X1Y X2Y X3Y

文書化する価値があるのは、それらをネストできることです(たとえば、最初のものは最初のものを閉じ}ません)。{

シェルは、それぞれのクロージングに順番に作用するなど、内側のブレースを最初に解決することを選択できたと思います}

  1. X{{a..c},{1..3}}
  2. X{a,{1..3}}Y X{b,{1..3}}Y X{c,{1..3}}Y

    (それはにA{a..c}B展開されAaB AbB AcB、ここAX{およびB,{1..3}Y

  3. X{a,1}Y X{a,2}Y X{a,3}Y X{b,1}Y X{b,2}Y X{b,3}Y X{c,1}Y X{c,2}Y X{c,3}Y

  4. XaY X1Y XaY Xa2...

しかし、私は特に直感的でも有用でもないと思います(たとえば、コメントのケビンの例を参照してください)、拡張が行われる順序についてはまだ曖昧さがありますが、それはそうではありませんcsh(ブレースを導入したシェル70年代後半の拡大、一方、{1..3}フォームが後で来た(1995年)からzsh{a..c}、まだ後に(2004年)からはbash)それをやりました。

注ことをcsh(最初から、参照2BSD(1979)のmanページはブレースの展開は入れ子にすることができたという事実文書をやった)、しかし、明示的に展開されるだろうか、ネストされたブレース展開言いませんでした。しかし、1979年cshコードを見て、それがどのように行われたかを確認できます。実際にネストを明示的に処理する方法と、外側の中括弧から始まる解決方法を参照してください。

いずれにせよ、の拡張{a..c},{1..3}がどのように影響を与える可能性があるのか、私は本当に見ていません。そこで,は、ブレース展開の演算子ではありません(ブレース内にないため)。通常の文字と同様に扱われます。


外側のブレースが内側のブレースの前に解決されることになっていることは私には奇妙に思えます。
ホークレイジング

@stéphane-chazelasこの式が解析される可能性のある2つの明白な方法があります。なぜ一方が解析され、もう一方が解析されないのですか?あなたのコメントは説明を与えていないようです。
イガル

だから、その説明は理にかなっていますが、これが「...に展開されていると文書化されている」場合、URLはありますか?
キセノイド

@xenoid更新されたソリューションをご覧ください。
イガル

1
@(everyone):展開を考慮し/dev/{h,s}d{a..d}{1..4,}ます。今ではそれも含めて拡張するとします/dev/null/dev/zero。ブレースの展開が完全に機能する場合、その展開は本当に面倒です。しかし、外部から機能するため、非常に簡単です/dev/{null,zero,{h,s}d{a..d}{1..4,}}
ケビン

7

これが簡単な答えです。最初の式では、コンマが区切り文字として使用されるため、ブレースの展開は、ネストされた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マニュアルには完全な定義が記載されていないことに注意してください。

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

ソースには、いくつかの便利なコメントが含まれています。最初は、ブレース展開アルゴリズムの高レベルの概要です。

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、ポストアンブルで関数が再帰的に呼び出されることもわかります。これにより、アルゴリズムの完全な説明が得られます。

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

参考までに、Bash Hackers Wikiをご覧ください。結合と入れ子のセクションでは問題に対処していませんが、ページにはブレース展開の構文/文法が記載されています。構文は次のパターンで指定されます。

{string1,string2,...,stringN}

{<START>..<END>}

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

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

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

そして、解析は次のように説明されます。

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

別のリファレンスについては、次のようなBash Beginner's Guideをご覧ください。

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の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

2

私の理解はこれです:

内側のブレースが(常に)最初に解決されます。

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

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

,は中括弧内にあるため、中括弧の要素を分離するだけです。

しかし、

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

,中括弧は、それが両側にブレース置換を引き起こして通常の文字、すなわち内にありません。


だから、{a..c}のいずれかに解決さa,b,ca b c湿度、ダウ・ジョーンズに依存?きちんとした。
クバンチク

これは少しわかりにくいようです。場合{{a..c},{1..3}}と同じである{a,b,c,1,2,3}、そしてべきではない{{a..c}.{1..3}}と同じこと{a,b,c.1,2,3}?もちろんこれは事実ではありません。
-ilkkachu

@ilkkachuなぜそれは同じである必要がありますか?,ブレース展開分離文字です、そうで.はありません。なぜ普通のキャラクターが特別なキャラクターのような同じ結果につながるのでしょうか?c.1は中括弧要素です。しかし、中に左右のブレース展開のためのアンカーです。外側の括弧は、ブレース展開のために使用されていると、その内容は、ブレース展開の形式を持っているため、その内容は、その形式を持っていないため、彼らはありません。{a..c}.{1..3}.,.
ハウケレイジング

@HaukeLaging、まあ、と{{a..c},{1..3}}なると、との{a,b,c,1,2,3}aにカンマが現れただけです。なぜ同じように表示されないのですか?@kubanczykのコメントはほぼ同じですが、カンマがそのように表示される場合、展開がコンマを生成するときとしないときをどのように知ることができますか?もちろん、答えは、それ自体ではコンマを生成せず、単語のリストを生成することです。だから、何もまたはに変わりません。bc{a..c}.{1..3}{a,b,c,1,2,3}{a,b,c.1,2,3}
-ilkkachu

@kubanczykわからない答えをからかってはいけません。
ホークレイジング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.