Applicare l'espansione del controvento in "ordine inverso"


21

Ad esempio si {a..c}{1..3}espande a a1 a2 a3 b1 b2 b3 c1 c2 c3.

Se volessi stampare a1 b1 c1 a2 b2 c2 a3 b3 c3, c'è un modo analogo per farlo? Qual è il modo più semplice?

Risposte:


30

Potresti fare:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Che poi dice alla shell di valutare:

echo {a..c}1 {a..c}2 {a..c}3

10

Per questo caso particolare, penso che l'opzione data da Stéphane Chazelas sia la migliore.

D'altra parte, quando espandi cose più complesse, questa opzione non si adatta bene. Quindi, puoi ottenere lo stesso con questo:

$ printf '%s\0' {a..c}{1..3} | sort -zk 1.2,1.2 | tr '\0' ' '

che ritorna:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Sembra un po 'disordinato, ma ora ho un enorme controllo nell'ordine, cambiando solo due caratteri nel comando sopra; per esempio:

$ echo {a..b}{1..2}{a..b}{1..2}

questo si espanderà a:

a1a1 a1a2 a1b1 a1b2 a2a1 a2a2 a2b1 a2b2 b1a1 b1a2 b1b1 b1b2 b2a1 b2a2 b2b1 b2b2

Supponiamo che io voglia tutto 1nella seconda espansione, quindi 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.2 | tr '\0' ' '
a1a1 a1a2 a1b1 a1b2 b1a1 b1a2 b1b1 b1b2 a2a1 a2a2 a2b1 a2b2 b2a1 b2a2 b2b1 b2b2

Supponiamo che io voglia tutto anella terza espansione, quindi il b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.3,1.3 | tr '\0' ' '
a1a1 a1a2 a2a1 a2a2 b1a1 b1a2 b2a1 b2a2 a1b1 a1b2 a2b1 a2b2 b1b1 b1b2 b2b1 b2b2

Supponiamo che io voglia tutto 1nella quarta espansione, quindi il 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.4,1.4 | tr '\0' ' '
a1a1 a1b1 a2a1 a2b1 b1a1 b1b1 b2a1 b2b1 a1a2 a1b2 a2a2 a2b2 b1a2 b1b2 b2a2 b2b2

Supponiamo che io voglia tutto 1anel mezzo, quindi 1b, quindi 2a, quindi 2b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.3 | tr '\0' ' '
a1a1 a1a2 b1a1 b1a2 a1b1 a1b2 b1b1 b1b2 a2a1 a2a2 b2a1 b2a2 a2b1 a2b2 b2b1 b2b2

Puoi anche, altrettanto facilmente, invertire qualsiasi ordine nelle espansioni sopra, semplicemente aggiungendo un ral comando precedente; ad esempio l'ultimo:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -rzk 1.2,1.3 | tr '\0' ' '
b2b2 b2b1 a2b2 a2b1 b2a2 b2a1 a2a2 a2a1 b1b2 b1b1 a1b2 a1b1 b1a2 b1a1 a1a2 a1a1

Nota_1 : di solito, se questa espansione finale verrà utilizzata come un elenco di argomenti, lo spazio finale non è un problema; ma se vuoi sbarazzartene, puoi aggiungere, ad esempio, uno dei comandi sopra| sed 's/ $//' ; o anche| sed 's/ $/\n/', per cambiare quello spazio finale per anewline

Note_2 : Negli esempi precedenti, ho usato sottoinsiemi di due elementi (ad esempio: {a, b} e {1,2} ) solo per semplicità nella dimostrazione del concetto: è possibile utilizzare sottoinsiemi di qualsiasi lunghezza finita, e il comando corrispondente, sarebbe comparabile.


5

bash, ksh, zsh

Un solo liner che funziona in (bash, ksh, zsh) (non tutte le shell possono fare "espansione del controvento" in ordine inverso):

$ echo {3..1}{c..a} | rev
a1 b1 c1 a2 b2 c2 a3 b3 c3

Un'alternativa che usa eval(che è ancora per bash, ksh, zsh e può essere più criptica) è:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Per capire cosa succede, sostituiscilo evalcon echo:

$ echo echo '{a..c}'{1..3}
echo {a..c}1 {a..c}2 {a..c}3

Il comando eseguito (dopo l'espansione eval) è in realtà echo {a..c}1 {a..c}2 {a..c}3. Che si espande come vuoi / hai bisogno.

tutte le conchiglie

Esistono diverse shell senza "espansioni di controventi", quindi non è possibile utilizzarle per "tutte le shell". Abbiamo bisogno di un ciclo (con uno spazio bianco finale):

$ for i in 1 2 3; do for j in a b c; do printf "%s%s " "$j" "$i"; done; done; echo
a1 b1 c1 a2 b2 c2 a3 b3 c3 

Se non è necessario aggiungere spazio finale:

s=""
for i in 1 2 3; do
    for j in a b c; do
        printf "%s%s%s" "$s" "$j" "$i"
        s=" "
    done
done
echo

stampe

a1 b1 c1 a2 b2 c2 a3 b3 c3

Se è necessario eseguire questa operazione per molti valori, è necessario utilizzare qualcosa di simile all'espansione del controvento per generare un elenco di numeri $(seq 10). E, poiché seq non può generare un elenco di lettere, dobbiamo convertire in ascii i numeri generati:

s=""
for i in $(seq 4); do
    for j in $(seq 5); do
        printf "%s\\$(printf %03o $((96+j)))%s" "$s" "$i"
        s=" "
    done
done
echo

stampe:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4

Puoi anche aggiungere yash -o braceexpandall'elenco.
Stéphane Chazelas,

@ StéphaneChazelas Non sono sicuro che dovrei. Il comando yash -o braceexpand -c 'echo {3..1}{c..a}'stampa 3{c..a} 2{c..a} 1{c..a}in linux. Non è una "espansione di rinforzo" completa.
Isaac,

3
{a..c}1 {a..c}2 {a..c}3

Le espansioni di parentesi graffe {a..c}{1..3}vengono espanse da sinistra a destra, quindi prima ottieni a{1..3} b{1..3} c{1..3}e poi le lettere vengono combinate con i numeri in a1 a2 a3 b1 b2 b3 c1 c2 c3. Per ottenere l'ordine desiderato, dovrai usare l'espressione leggermente più lunga sopra.


Se volessi farlo per una vasta gamma di "numeri" non sarebbe più pratico.
RUBEN GONÇALO MOROUÇO,

3
@ RUBENGONÇALOMOROUÇO No, e se lo stai facendo per una vasta gamma di numeri, suggerirei di utilizzare un approccio alternativo, come un doppio loop. Funzionerebbe per molte migliaia di combinazioni, mentre una parentesi allarga il mio "elenco di argomenti troppo lungo" in determinati contesti.
Kusalananda

2

Utilizzando un loop:

for n in {1..3}; do printf '%s\n' {a..c}"$n"; done

Questo passerà attraverso la tua prima espansione e quindi espanderà ogni personaggio con la seconda.

Se hai bisogno dell'output tutto su una riga puoi rimuovere \n:

for n in {1..3}; do printf '%s ' {a..c}"$n"; done

Questo non ti darà una nuova riga finale, ma se lo stai passando a un comando o una variabile non dovrebbe essere un problema.


1
Perché così tanti voti negativi per una soluzione ad anello?
Isaac,

Duh, devo aver letto male la domanda. Aggiornato
Jesse_b il

2

Questo funziona per il tuo caso semplice e può essere esteso, ma sfuggirebbe rapidamente alla mano. I casi più complessi per i quali questo non funzionerebbe sono facili da costruire.

Invertire l'ordine delle espansioni del controvento, quindi scambiare i caratteri:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'

@muru: Oops. Fisso. Grazie.
In pausa fino a nuovo avviso.

@Isaac: ho corretto lo spazio finale.
In pausa fino a nuovo avviso.

0

Un metodo semplice sarebbe usare l'ordinamento (1.2.1.2 significa che prendi un carattere nella seconda posizione e finisci nello stesso posto).

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2
a1
b1
c1
a2
b2
c2
a3
b3
c3

Se li vuoi in una riga, puoi usare tr in questo modo:

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2|tr '\n' ' '
a1 b1 c1 a2 b2 c2 a3 b3 c3

-2

Fatto con il metodo seguente

for i in {1..10}; do for j in {a..c}; do echo $j$i; done; done| perl -pne "s/\n/ /g"

produzione

a1 b1 c1 a2 b2 c2 a3 b3 c3 a4 b4 c4 a5 b5 c5 a6 b6 c6 a7 b7 c7 a8 b8 c8 a9 b9 c9 a10 b10 c10

considera anchefor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.