Ecco la risposta breve. Nella prima espressione la virgola viene utilizzata come separatore, quindi l'espansione del controvento è solo la concatenazione delle due sottoespressioni nidificate. Nella seconda espressione la virgola viene essa stessa trattata come una sottoespressione a carattere singolo, quindi si formano espressioni di prodotto .
Ciò che mancava era la definizione di come vengono eseguite le espansioni del tutore. Ecco tre riferimenti:
Segue una spiegazione più dettagliata.
Hai confrontato il risultato di questa espressione:
$ echo {{a..c},{1..3}}
a b c 1 2 3
al risultato di questa espressione:
$ echo {a..c},{1..3}
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
Dici che è difficile da spiegare, cioè che è controintuitivo. Ciò che manca è una definizione formale di come vengono elaborate le espansioni del tutore. Si nota che il Manuale di Bash non fornisce una definizione completa.
Ho cercato un po 'ma non sono riuscito a trovare la definizione mancante (completa, formale). Quindi sono andato al codice sorgente:
La fonte contiene un paio di commenti utili. La prima è una panoramica di alto livello dell'algoritmo di espansione del controvento:
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.
Quindi il formato di un token di espansione del controvento è il seguente:
<PREAMBLE><AMBLE><POSTAMBLE>
Il principale punto di accesso all'espansione è una funzione chiamata brace_expand
che è descritta come segue:
Return an array of strings; the brace expansion of TEXT.
Quindi la brace_expand
funzione prende una stringa che rappresenta un'espressione di espansione del controvento e restituisce l'array di stringhe espanse.
Combinando queste due osservazioni vediamo che l'ampolla viene espansa in un elenco di stringhe, ciascuna delle quali è concatenata nel preambolo. Il postambolo viene quindi espanso in un elenco di stringhe e ogni stringa nell'elenco di postamble viene concatenata su ciascuna stringa nell'elenco dei preamboli / amble (ovvero viene formato il prodotto dei due elenchi). Ma questo non descrive come vengono elaborati l'ambra e il postamble. Fortunatamente c'è un commento che descrive anche quello. L'amble viene elaborato da una funzione chiamata la expand_amble
cui definizione è preceduta dal seguente commento:
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.
Altrove nel codice vediamo che BRACE_ARG_SEPARATOR è definito come una virgola. Ciò chiarisce che l'ampolla è un elenco di stringhe separato da virgole, alcune delle quali possono anche essere espressioni di espansione di parentesi graffe. Queste stringhe formano quindi un singolo array. Infine, possiamo anche vedere che dopo expand_amble
viene chiamata la brace_expand
funzione viene quindi chiamata ricorsivamente sul postamble. Questo ci dà una descrizione completa dell'algoritmo.
Ci sono altri riferimenti (non ufficiali) che confermano questa scoperta.
Per un riferimento, controlla il Wiki di Bash Hackers . La sezione sulla combinazione e l'annidamento non risolve del tutto il tuo problema, ma la pagina fornisce la sintassi / grammatica dell'espansione del controvento, che penso risponda alla tua domanda. La sintassi è data dai seguenti modelli:
{string1,string2,...,stringN}
{<START>..<END>}
<PREAMBLE>{........}
{........}<POSTSCRIPT>
<PREAMBLE>{........}<POSTSCRIPT>
E l'analisi è descritta come segue:
L'espansione del controvento viene utilizzata per generare stringhe arbitrarie. Le stringhe specificate vengono utilizzate per generare tutte le possibili combinazioni con i preamboli e i postscript circostanti opzionali.
Per un altro riferimento, dai un'occhiata alla Bash Beginner's Guide , che ha il seguente da dire:
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.
Quindi per analizzare le espressioni di espansione di parentesi si va da sinistra a destra, espandendo ogni espressione e formando prodotti successivi (rispetto all'operazione di concatenazione di stringhe).
Consideriamo ora la tua prima espressione:
{{a..c},{1..3}}
Nella lingua del Wiki di Bash Hacker, questo corrisponde alla prima forma:
{string1,string2,...,stringN}
Dove N=2
, string1={a..c}
e string2={1..3}
- le espansioni di rinforzo interne essendo eseguite prima e ciascuno dei quali è della forma {<START>..<END>}
. In alternativa, possiamo dire che questa è un'espressione di espansione di parentesi graffa che consiste solo di un'ambientazione (nessun preambolo o postamble). L'ampolla è un elenco separato da virgole, quindi esaminiamo l'elenco uno slot alla volta ed eseguiamo ulteriori espansioni dove richiesto. Nessun prodotto è formato perché non sono presenti espressioni adiacenti (la virgola viene utilizzata come separatore).
Ora diamo un'occhiata alla tua seconda espressione:
{a..c},{1..3}
Nel linguaggio del Wiki di Bash Hacker, questa espressione corrisponde alla forma:
{........}<POSTSCRIPT>
dove postscript è la sottoespressione ,{1..3}
. In alternativa, possiamo dire che questa espressione ha un amble ( {a..c}
) e un postamble ( ,{1..3}
). L'ampolla viene espansa nell'elenco a b c
e quindi ciascuna di queste viene concatenata con ciascuna delle stringhe nell'espansione del postambolo. Il postamble viene elaborato in modo ricorsivo: ha un preambolo di ,
e un amble di {1..3}
. Questo è espanso all'elenco ,1 ,2 ,3
. Le due liste a b c
e ,1 ,2 ,3
vengono poi combinati per formare l'elenco dei prodotti a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3
.
Potrebbe essere utile fornire una descrizione psico-algebrica di come vengono analizzate queste espressioni, dove le parentesi "[]" indicano array, "+" indica la concatenazione di array e "*" indica il prodotto cartesiano (rispetto alla concatenazione).
Ecco come viene espansa la prima espressione (un passo per riga):
{{a..c},{1..3}}
{a..c} + {1..3}
[a b c] + [1 2 3]
a b c 1 2 3
Ed ecco come viene espansa la seconda espressione:
{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