Cóż, jest rozwikłana jedna warstwa na raz:
X{{a..c},{1..3}}Y
udokumentowano jako rozszerzona X{a..c}Y
X{1..3}Y
(tak X{A,B}Y
rozszerzona XA
XB
przy A
czym {a..c}
i B
jest {1..3}
), dokumentuje się jako rozszerzony XaY
XbY
XcY
X1Y
X2Y
X3Y
.
Udokumentować może to, że można je zagnieżdżać (że pierwszy }
nie zamyka na przykład pierwszego {
w nim).
Przypuszczam, że pociski mogły zdecydować się najpierw rozwiązać wewnętrzne nawiasy klamrowe, tak jak działając kolejno po każdym zamknięciu }
:
X{{a..c},{1..3}}
X{a,{1..3}}Y
X{b,{1..3}}Y
X{c,{1..3}}Y
(to jest A{a..c}B
rozwinięte do AaB
AbB
AcB
, gdzie A
jest X{
i B
jest ,{1..3}Y
)
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
XaY
X1Y
XaY
Xa2
...
Ale nie uważam tego za bardziej intuicyjne ani użyteczne (patrz na przykład przykład Kevina w komentarzach), nadal istniałaby pewna dwuznaczność co do kolejności, w jakiej byłyby wykonywane ekspansje, i nie w ten sposób csh
(powłoka, która wprowadziła nawias klamrowy) ekspansja pod koniec lat 70., a {1..3}
forma pojawiła się później (1995) zsh
i {a..c}
jeszcze później (2004) od bash
).
Zauważ, że csh
(od samego początku, patrz strona podręcznika 2BSD (1979) ) udokumentowano fakt, że rozszerzenia nawiasów klamrowych mogą być zagnieżdżone, chociaż nie powiedział wprost, w jaki sposób zagnieżdżone rozszerzenia nawiasów klamrowych będą rozwijane. Ale możesz spojrzeć na csh
kod z 1979 roku, aby zobaczyć, jak to zostało zrobione. Zobacz, jak rzeczywiście radzi sobie z zagnieżdżaniem i jak to rozwiązać, zaczynając od zewnętrznych nawiasów klamrowych.
W każdym razie tak naprawdę nie widzę, w jaki sposób rozszerzenie {a..c},{1..3}
może mieć jakieś łożysko. Tam ,
nie jest operatorem rozwinięcia nawiasu (ponieważ nie ma go w nawiasach), więc jest traktowany jak każdy zwykły znak.
/dev/{h,s}d{a..d}{1..4,}
. Załóżmy teraz, że chcesz go rozszerzyć, tak aby obejmował także/dev/null
i/dev/zero
. Jeśli ekspansja nawiasów zadziałałaby od wewnątrz, taka rozbudowa byłaby naprawdę denerwująca. Ale ponieważ działa z zewnątrz, jest dość trywialny:/dev/{null,zero,{h,s}d{a..d}{1..4,}}
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:
do wyniku tego wyrażenia:
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:
Format tokenu rozwijania nawiasów jest więc następujący:
Głównym punktem wejścia do ekspansji jest wywoływana funkcja,
brace_expand
która jest opisana następująco: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: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łaniubrace_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:
Analiza składniowa jest opisana następująco:
W celu uzyskania dalszych informacji zajrzyj do Przewodnika dla początkujących Bash , który ma następujące zdanie:
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:
W języku Wiki Bash Hacker odpowiada to pierwszej formie:
Gdzie
N=2
,string1={a..c}
istring2={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:
W języku Wiki Bash Hacker to wyrażenie odpowiada formie:
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ówa,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 oto jak rozszerza się drugie wyrażenie:
źródło
Rozumiem to:
Wewnętrzne nawiasy klamrowe są rozwiązywane jako pierwsze (jak zawsze), które się obracają
w
Ponieważ
,
znajduje się w nawiasach klamrowych, po prostu oddziela elementy nawiasów klamrowych.Ale w przypadku
,
nie jest w szelki czyli jest zwykłą postać powodując permutacje usztywnień po obu stronach.źródło
{a..c}
albo rozwiązuje,a,b,c
alboa b c
zależy od wilgotności i Dow Jones? Schludny.{{a..c},{1..3}}
jest taki sam jak{a,b,c,1,2,3}
, to nie powinien{{a..c}.{1..3}}
być taki sam jak{a,b,c.1,2,3}
? Tak oczywiście nie jest.,
jest znakiem rozdzielającym nawias klamrowy,.
nie jest. Dlaczego zwykła postać powinna prowadzić do takich samych rezultatów jak szczególny?c.1
jest elementem klamrowym. Ale w{a..c}.{1..3}
tym.
jest kotwica dla nawiasów klamrowych po lewej i prawej stronie. Z,
zewnętrznymi nawiasami klamrowymi są używane do rozwijania nawiasów klamrowych, ponieważ ich treść ma format rozwijania nawiasów klamrowych, a.
nie są, ponieważ ich treść nie ma tego formatu.{{a..c},{1..3}}
zamieni się w,{a,b,c,1,2,3}
wówczas pojawią się przecinki międzya
,b
ac
. Dlaczego nie pojawią się w taki sam sposób{a..c}.{1..3}
? Komentarz @kubańczyk dotyczy tego samego, jeśli przecinki pojawiają się w ten sposób, skąd wiemy, kiedy rozszerzenie generuje przecinki, a kiedy nie? Odpowiedzią jest oczywiście to, że nigdy nie generuje przecinków, generuje listę słów. Więc nic nie zmienia się w{a,b,c,1,2,3}
lub{a,b,c.1,2,3}
.