Zastosuj rozwinięcie nawiasu w „odwrotnej kolejności”


21

Na przykład {a..c}{1..3}rozwija się do a1 a2 a3 b1 b2 b3 c1 c2 c3.

Jeśli chciałbym wydrukować a1 b1 c1 a2 b2 c2 a3 b3 c3, czy istnieje analogiczny sposób na zrobienie tego? Jaki jest najprostszy sposób?

Odpowiedzi:


30

Mógłbyś:

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

Co następnie każe powłoce ocenić:

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

10

W tym konkretnym przypadku uważam, że opcja podana przez Stéphane'a Chazelasa jest najlepsza.

Z drugiej strony, gdy rozwijasz bardziej złożone rzeczy, ta opcja nie skaluje się dobrze. Dzięki temu możesz osiągnąć to samo:

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

który zwraca:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Wygląda na trochę bałaganu, ale teraz mam ogromną kontrolę nad kolejnością, zmieniając tylko dwa znaki w powyższym poleceniu; na przykład:

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

rozwinie się to do:

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

Załóżmy, że chcę wszystkich 1w drugim rozszerzeniu, a następnie 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

Załóżmy, że chcę wszystkich aw trzecim rozszerzeniu, a następnie 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

Załóżmy, że chcę wszystkich 1w czwartym rozszerzeniu, a następnie 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

Załóżmy, że chcę wszystko 1aw środku, potem 1b, potem 2a, potem 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

Możesz nawet, równie łatwo, odwrócić dowolną kolejność w powyższych rozszerzeniach, dodając po prostu rdo poprzedniego polecenia; na przykład ostatni:

$ 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

Uwaga_1 : zazwyczaj, jeśli to końcowe rozwinięcie ma być użyte jako lista argumentów, spacja końcowa nie stanowi problemu; ale jeśli chcesz się go pozbyć, możesz na przykład dodać dowolne z powyższych poleceń| sed 's/ $//' ; lub nawet| sed 's/ $/\n/', aby zmienić tę końcową przestrzeń dlanewline

Uwaga_2 : W powyższych przykładach użyłem podzbiorów dwóch elementów (tj .: {a, b} i {1,2} ) tylko dla uproszczenia w dowodzie koncepcji: możesz użyć podzbiorów o dowolnej skończonej długości, oraz odpowiednie polecenie byłoby porównywalne.


5

bash, ksh, zsh

Jeden linijka, która działa w (bash, ksh, zsh) (nie wszystkie powłoki mogą wykonywać „rozwijanie nawiasu klamrowego” w odwrotnej kolejności):

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

Alternatywą, która używa eval(która wciąż jest dla bash, ksh, zsh i może być bardziej tajemnicza) jest:

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

Aby zrozumieć, co się dzieje, wymienić evalz echo:

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

Wykonane polecenie (po rozszerzeniu eval) jest w rzeczywistości echo {a..c}1 {a..c}2 {a..c}3. Który rozwija się tak, jak chcesz / potrzebujesz.

wszystkie muszle

Istnieje kilka powłok bez „rozszerzeń nawiasów”, więc nie można użyć tego dla „wszystkich powłok”. Potrzebujemy pętli (z końcową białą spacją):

$ 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 

Jeśli nie musisz dodawać końcowego miejsca:

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

Wydruki

a1 b1 c1 a2 b2 c2 a3 b3 c3

JEŚLI musisz to zrobić dla wielu wartości, musimy użyć czegoś podobnego do rozszerzenia nawiasów, aby wygenerować listę liczb $(seq 10). Ponieważ seq nie może wygenerować listy liter, musimy przekonwertować na ascii wygenerowane liczby:

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

drukuje:

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

Możesz także dodać yash -o braceexpanddo listy.
Stéphane Chazelas,

@ StéphaneChazelas Nie jestem pewien, czy powinienem. Polecenie jest yash -o braceexpand -c 'echo {3..1}{c..a}'drukowane 3{c..a} 2{c..a} 1{c..a}w systemie Linux. Nie pełne „rozszerzenie nawiasu klamrowego”.
Isaac

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

Rozszerzenia nawiasów {a..c}{1..3}są rozszerzane od lewej do prawej, więc najpierw dostajesz, a{1..3} b{1..3} c{1..3}a następnie litery są łączone z cyframi w a1 a2 a3 b1 b2 b3 c1 c2 c3. Aby uzyskać pożądaną kolejność, będziesz musiał użyć nieco dłuższego wyrażenia powyżej.


Jeśli chcesz to zrobić dla szerokiego zakresu „liczb”, nie byłoby to już praktyczne.
RUBEN GONÇALO MOROUÇO

3
@ RUBENGONÇALOMOROUÇO Nie, nie zrobiłby tego, a jeśli robisz to dla dużej liczby liczb, sugerowałbym zastosowanie alternatywnego podejścia, takiego jak podwójna pętla. To działałoby dla wielu tysięcy kombinacji, podczas gdy nawias klamrowy rozszerza moją „listę argumentów zbyt długą” w niektórych kontekstach.
Kusalananda

2

Za pomocą pętli:

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

Spowoduje to zapętlenie pierwszego rozszerzenia, a następnie rozszerzenie każdej postaci drugim.

Jeśli potrzebujesz danych wyjściowych w jednym wierszu, możesz usunąć \n:

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

To nie da ci końca nowej linii, ale jeśli przekazujesz ją do polecenia lub zmiennej, nie powinno to stanowić problemu.


1
Po co tyle głosów za rozwiązaniem pętli?
Izaak

Chyba źle przeczytałem pytanie. Zaktualizowano
Jesse_b

2

Działa to w przypadku prostej skrzynki i można ją przedłużyć, ale szybko wymknie się spod kontroli. Bardziej złożone przypadki, dla których to nie zadziała, są łatwe do zbudowania.

Odwróć kolejność rozszerzeń nawiasów, a następnie zamień znaki:

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

@muru: Ups. Naprawiony. Dzięki.
Wstrzymano do odwołania.

@Isaac: Naprawiłem spację końcową.
Wstrzymano do odwołania.

0

Jedną z prostych metod byłoby użycie sortowania (1.2,1.2 oznacza, że ​​bierzesz jedną postać z drugiej pozycji i kończysz w tym samym miejscu).

$ 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

Jeśli chcesz je w jednym wierszu, możesz użyć tr tak:

$ 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

Wykonano poniższą metodą

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

wydajność

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

rozważ równieżfor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.