Anwenden der Klammererweiterung in umgekehrter Reihenfolge

21

Zum Beispiel {a..c}{1..3}erweitert um a1 a2 a3 b1 b2 b3 c1 c2 c3.

Wenn ich drucken wollte a1 b1 c1 a2 b2 c2 a3 b3 c3, gibt es eine analoge Möglichkeit, dies zu tun? Was ist der einfachste Weg?

RUBEN GONÇALO MOROUÇO
quelle

Antworten:

30

Du könntest es tun:

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

Was dann der Shell sagt, dass sie auswerten soll:

echo {a..c}1 {a..c}2 {a..c}3
Stéphane Chazelas
quelle
10

Für diesen speziellen Fall denke ich, dass die Option von Stéphane Chazelas gegeben für die beste.

Wenn Sie dagegen komplexere Dinge erweitern, lässt sich diese Option nicht gut skalieren. So können Sie dasselbe erreichen:

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

was zurückgibt:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Scheint ein wenig chaotisch zu sein, aber jetzt habe ich eine große Kontrolle in der Reihenfolge und ändere nur zwei Zeichen im obigen Befehl. beispielsweise:

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

dies wird sich ausdehnen auf:

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

Angenommen, ich möchte alle 1in der zweiten Erweiterung, dann die 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

Angenommen, ich möchte alle ain der dritten Erweiterung, dann die 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

Angenommen, ich möchte alle 1in der vierten Erweiterung, dann die 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

Angenommen, ich möchte alles 1ain der Mitte haben, dann 1b, dann 2a, dann 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

Genauso einfach können Sie die Reihenfolge in den obigen Erweiterungen umkehren, indem Sie rdem vorherigen Befehl einfach ein hinzufügen . Zum Beispiel die letzte:

$ 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

Anmerkung_1 : Wenn diese letzte Erweiterung als Liste von Argumenten verwendet wird, ist das nachfolgende Leerzeichen normalerweise kein Problem. aber wenn Sie es loswerden möchten, können Sie zum Beispiel einen der obigen Befehle hinzufügen| sed 's/ $//' ; oder sogar| sed 's/ $/\n/', um diesen abschließenden Raum für a zu ändernnewline

Anmerkung_2 : In den obigen Beispielen habe ichzur Vereinfachung des Proof of ConceptTeilmengen von zwei Elementen (dh {a, b} und {1,2} ) verwendet: Sie können Teilmengen beliebiger endlicher Länge verwenden entsprechender Befehl, wäre vergleichbar.

matsib.dev
quelle
5

bash, ksh, zsh

Ein Einzeiler, der in (bash, ksh, zsh) funktioniert (nicht alle Shells können die "Klammererweiterung" in umgekehrter Reihenfolge ausführen):

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

Eine Alternative, die verwendet wird eval(die immer noch für bash, ksh, zsh und möglicherweise kryptischer ist), ist:

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

Um zu verstehen, was passiert, ersetzen Sie evaldurch echo:

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

Der ausgeführte Befehl (nach eval expansion) ist tatsächlich echo {a..c}1 {a..c}2 {a..c}3. Welches erweitert, wie Sie wollen / brauchen.

alle Muscheln

Es gibt mehrere Shells ohne "Klammererweiterungen", daher ist es nicht möglich, diese für "alle Shells" zu verwenden. Wir brauchen eine Schleife (mit einem nachgestellten Leerzeichen):

$ 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 

Wenn Sie keinen abschließenden Speicherplatz hinzufügen müssen:

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

Druckt

a1 b1 c1 a2 b2 c2 a3 b3 c3

WENN Sie dies für viele Werte tun müssen, müssen Sie etwas Ähnliches wie die geschweifte Klammer verwenden, um eine Liste von Zahlen zu generieren $(seq 10). Und da seq keine Liste von Buchstaben generieren kann, müssen wir die generierten Zahlen in ASCII konvertieren:

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

druckt:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4
Isaac
quelle
Sie können yash -o braceexpandder Liste auch hinzufügen .
Stéphane Chazelas
@ StéphaneChazelas Ich bin mir nicht sicher, ob ich sollte. Der Befehl wird unter Linux yash -o braceexpand -c 'echo {3..1}{c..a}'gedruckt 3{c..a} 2{c..a} 1{c..a}. Keine vollständige "Klammererweiterung".
Isaac
3
{a..c}1 {a..c}2 {a..c}3

Die Klammererweiterungen in {a..c}{1..3}werden von links nach rechts erweitert, sodass Sie zuerst a{1..3} b{1..3} c{1..3}die Buchstaben erhalten und dann mit den Zahlen in kombiniert werden a1 a2 a3 b1 b2 b3 c1 c2 c3. Um die gewünschte Reihenfolge zu erhalten, müssen Sie den obigen etwas längeren Ausdruck verwenden.

Kusalananda
quelle
Wenn Sie es für einen großen Bereich von "Zahlen" tun wollten, wäre es nicht mehr praktisch.
RUBEN GONÇALO MOROUÇO
3
@ RUBENGONÇALOMOROUÇO Nein, würde es nicht, und wenn Sie es für einen großen Bereich von Zahlen tun, würde ich vorschlagen, einen alternativen Ansatz zu verwenden, wie eine Doppelschleife. Das würde für viele tausend Kombinationen funktionieren, während eine geschweifte Klammer meine "Argumentliste zu lang" in bestimmten Zusammenhängen erweitert.
Kusalananda
2

Verwenden einer Schleife:

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

Dies durchläuft Ihre erste Erweiterung und erweitert dann jedes Zeichen mit dem zweiten.

Wenn Sie die Ausgabe in einer Zeile benötigen, können Sie Folgendes entfernen \n:

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

Dies gibt Ihnen keinen nachgestellten Zeilenumbruch, aber wenn Sie ihn an einen Befehl oder eine Variable übergeben, sollte dies kein Problem sein.

Jesse_b
quelle
1
Warum stimmen so viele für eine Loop-Lösung?
Isaac
Duh ich muss die Frage falsch gelesen haben. Aktualisiert
Jesse_b
2

Dies funktioniert für Ihren einfachen Fall und kann erweitert werden, aber es würde schnell außer Kontrolle geraten. Komplexere Fälle, für die dies nicht funktionieren würde, sind einfach zu konstruieren.

Kehren Sie die Reihenfolge der Klammererweiterungen um und tauschen Sie dann die Zeichen aus:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'
Bis auf weiteres angehalten.
quelle
@muru: Ups. Fest. Vielen Dank.
Bis auf weiteres angehalten.
@Isaac: Ich habe das Leerzeichen korrigiert.
Bis auf weiteres angehalten.
0

Eine einfache Methode wäre die Verwendung von sort (1.2.1.2 bedeutet, dass Sie ein Zeichen an der zweiten Position nehmen und am selben Ort enden).

$ 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

Wenn Sie sie in einer Zeile haben möchten, können Sie tr folgendermaßen verwenden:

$ 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
Hopfender Hase
quelle
-2

Geschehen nach folgender Methode

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

Ausgabe

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
Praveen Kumar BS
quelle
for i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller