Oto krótka odpowiedź. W pierwszym wyrażeniu przecinek jest używany jako separator, więc rozwinięcie nawiasu to po prostu połączenie dwóch zagnieżdżonych podwyrażeń. W drugim ekspresji przecinek kolei jest traktowany jako podwyrażenie pojedynczych znaków, tak wyrażenia produktu są uformowane.
Brakowało ci definicji sposobu wykonywania nawiasów klamrowych. Oto trzy referencje:
Bardziej szczegółowe wyjaśnienie następuje.
Porównałeś wynik tego wyrażenia:
$ echo {{a..c},{1..3}}
a b c 1 2 3
do wyniku tego wyrażenia:
$ echo {a..c},{1..3}
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
Mówisz, że trudno to wytłumaczyć, tzn. Że jest to sprzeczne z intuicją. Brakuje jednak formalnej definicji przetwarzania rozszerzeń nawiasów. Zauważ, że Bash Manual nie podaje pełnej definicji.
Szukałem trochę, ale nie znalazłem też brakującej (kompletnej, formalnej) definicji. Więc poszedłem do kodu źródłowego:
Źródło zawiera kilka przydatnych komentarzy. Pierwszy to ogólny przegląd algorytmu rozwijania nawiasów klamrowych:
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.
Format tokenu rozwijania nawiasów jest więc następujący:
<PREAMBLE><AMBLE><POSTAMBLE>
Głównym punktem wejścia do ekspansji jest wywoływana funkcja, brace_expand
która jest opisana następująco:
Return an array of strings; the brace expansion of TEXT.
Tak więc brace_expand
funkcja pobiera ciąg reprezentujący wyrażenie rozwijające nawias i zwraca tablicę rozwiniętych ciągów.
Łącząc te dwie obserwacje, widzimy, że kulka jest rozwinięta do listy ciągów, z których każda jest połączona z preambułą. Postamble jest następnie rozwijane do listy ciągów, a każdy ciąg z listy postamble jest konkatenowany z każdym ciągiem z listy preambuły / amble (tzn. Powstaje iloczyn dwóch list). Ale to nie opisuje, jak przetwarzana jest amble i postamble. Na szczęście jest też komentarz, który to opisuje. Kulka jest przetwarzana przez funkcję o nazwie, expand_amble
której definicja poprzedzona jest następującym komentarzem:
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.
W innym miejscu kodu widzimy, że BRACE_ARG_SEPARATOR jest zdefiniowany jako przecinek. To wyjaśnia, że amble jest rozdzieloną przecinkami listą ciągów znaków, z których niektóre mogą być również wyrażeniami rozwijającymi nawiasy klamrowe. Te ciągi następnie tworzą pojedynczą tablicę. Wreszcie możemy również zobaczyć, że po expand_amble
wywołaniu brace_expand
funkcja jest następnie wywoływana rekurencyjnie na postamble. To daje nam pełny opis algorytmu.
Istnieją inne (nieoficjalne) odniesienia, które potwierdzają to odkrycie.
Dla jednego odniesienia zajrzyj na Wiki Bash Hackers . Sekcja poświęcona łączeniu i zagnieżdżaniu nie całkiem rozwiązuje twój problem, ale strona zawiera składnię / gramatykę nawiasów klamrowych, które moim zdaniem odpowiadają na twoje pytanie. Składnia jest podawana przez następujące wzorce:
{string1,string2,...,stringN}
{<START>..<END>}
<PREAMBLE>{........}
{........}<POSTSCRIPT>
<PREAMBLE>{........}<POSTSCRIPT>
Analiza składniowa jest opisana następująco:
Rozwijanie nawiasów służy do generowania dowolnych ciągów. Podane ciągi znaków są używane do generowania wszystkich możliwych kombinacji z opcjonalnymi otaczającymi preambułami i postscripts.
W celu uzyskania dalszych informacji zajrzyj do Przewodnika dla początkujących Bash , który ma następujące zdanie:
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.
Aby przeanalizować wyrażenia nawiasów klamrowych, przechodzimy od lewej do prawej, rozszerzając każde wyrażenie i tworząc kolejne produkty (w odniesieniu do operacji konkatenacji łańcuchowej).
Rozważmy teraz twoje pierwsze wyrażenie:
{{a..c},{1..3}}
W języku Wiki Bash Hacker odpowiada to pierwszej formie:
{string1,string2,...,stringN}
Gdzie N=2
, string1={a..c}
i string2={1..3}
- wewnętrzne nawiasy wykonywane najpierw, a każdy z nich jest w formie {<START>..<END>}
. Alternatywnie możemy powiedzieć, że jest to wyrażenie rozwijane nawiasami klamrowymi, które składa się tylko z amble (bez preambuły ani postamble). Amble jest listą oddzieloną przecinkami, więc przeglądamy listę po jednym polu na raz i w razie potrzeby wykonujemy dodatkowe rozszerzenia. Nie powstaje żaden produkt, ponieważ nie ma sąsiednich wyrażeń (przecinek jest używany jako separator).
Następnie spójrzmy na twoje drugie wyrażenie:
{a..c},{1..3}
W języku Wiki Bash Hacker to wyrażenie odpowiada formie:
{........}<POSTSCRIPT>
gdzie postscript jest podwyrażeniem ,{1..3}
. Alternatywnie możemy powiedzieć, że to wyrażenie ma amble ( {a..c}
) i postamble ( ,{1..3}
). Kulka jest rozwijana do listy, a b c
a następnie każda z nich jest łączona z każdym ciągiem w rozwinięciu postamble. Postamble jest przetwarzany rekurencyjnie: ma preambułę ,
i ambulans {1..3}
. To jest rozwinięte do listy ,1 ,2 ,3
. Dwie listy, a b c
a ,1 ,2 ,3
następnie są łączone w celu utworzenia listy produktów a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
.
Może pomóc podać psuedo-algebraiczny opis tego, jak te wyrażenia są parsowane, gdzie nawiasy kwadratowe „[]” oznaczają tablice, „+” oznacza konkatenację macierzy, a „*” oznacza iloczyn kartezjański (w odniesieniu do konkatenacji).
Oto jak rozwija się pierwsze wyrażenie (jeden krok na linię):
{{a..c},{1..3}}
{a..c} + {1..3}
[a b c] + [1 2 3]
a b c 1 2 3
A oto jak rozszerza się drugie wyrażenie:
{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