Terapkan ekspansi brace dalam “urutan terbalik”

21

Misalnya {a..c}{1..3}diperluas ke a1 a2 a3 b1 b2 b3 c1 c2 c3.

Jika saya ingin mencetak a1 b1 c1 a2 b2 c2 a3 b3 c3, apakah ada cara analog untuk melakukannya? Apa cara paling sederhana?

RUBEN GONÇALO MOROUÇO
sumber

Jawaban:

30

Anda bisa melakukannya:

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

Yang kemudian memberitahu shell untuk mengevaluasi:

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

Untuk kasus khusus ini, saya pikir opsi yang diberikan oleh Stéphane Chazelas adalah yang terbaik.

Di sisi lain, ketika Anda memperluas hal-hal yang lebih kompleks, opsi ini tidak dapat diukur dengan baik. Jadi, Anda dapat mencapai hal yang sama dengan ini:

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

yang mengembalikan:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Tampaknya sedikit berantakan, tetapi sekarang, saya memiliki kontrol besar dalam pesanan, hanya mengubah dua karakter pada perintah di atas; sebagai contoh:

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

ini akan diperluas ke:

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

Misalkan saya ingin semua 1dalam ekspansi kedua, maka 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

Misalkan saya ingin semua aekspansi ketiga, maka 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

Misalkan saya ingin semua 1dalam ekspansi keempat, maka 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

Misalkan saya ingin semua 1adi tengah, lalu 1b, lalu 2a, kemudian 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

Anda bahkan dapat, dengan mudah, membalik urutan apa pun dalam ekspansi di atas, hanya menambahkan rperintah sebelumnya; misalnya, yang terakhir:

$ 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

Note_1 : biasanya, jika ekspansi akhir ini akan digunakan sebagai daftar argumen, ruang tambahan tidak menjadi masalah; tetapi jika Anda ingin menghilangkannya, Anda dapat menambahkan, ke salah satu perintah di atas, misalnya| sed 's/ $//' ; atau bahkan| sed 's/ $/\n/', untuk mengubah ruang tambahan itu untuk anewline

Note_2 : Dalam contoh di atas, saya telah menggunakan himpunan bagian dari dua elemen (yaitu: {a, b} dan {1,2} ) hanya untuk kesederhanaan dalam bukti konsep: Anda dapat menggunakan himpunan bagian dari panjang yang terbatas, dan perintah yang sesuai, akan sebanding.

matsib.dev
sumber
5

bash, ksh, zsh

Liner satu yang bekerja di (bash, ksh, zsh) (tidak semua shell dapat melakukan "ekspansi Brace" dalam urutan terbalik):

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

Alternatif yang digunakan eval (yang masih untuk bash, ksh, zsh dan mungkin lebih samar) adalah:

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

Untuk memahami apa yang terjadi, gantikan evaldenganecho :

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

Perintah dieksekusi (setelah ekspansi eval) sebenarnya echo {a..c}1 {a..c}2 {a..c}3. Yang mengembang sesuai keinginan / kebutuhan.

semua kerang

Ada beberapa shell tanpa "brace expansions", jadi, tidak mungkin menggunakannya untuk "semua shell". Kami membutuhkan sebuah loop (dengan spasi spasi tambahan):

$ 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 

Jika Anda tidak boleh menambahkan spasi tambahan:

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

Cetakan

a1 b1 c1 a2 b2 c2 a3 b3 c3

JIKA Anda perlu melakukan ini untuk banyak nilai kita perlu menggunakan sesuatu yang mirip dengan ekspansi penjepit untuk menghasilkan daftar angka $(seq 10). Dan, karena seq tidak dapat menghasilkan daftar huruf, kita perlu mengonversi ke ascii angka yang dihasilkan:

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

cetakan:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4
Ishak
sumber
Anda juga dapat menambahkan yash -o braceexpandke daftar.
Stéphane Chazelas
@ StéphaneChazelas Saya tidak yakin harus melakukannya. Perintah yash -o braceexpand -c 'echo {3..1}{c..a}'dicetak 3{c..a} 2{c..a} 1{c..a}di linux. Bukan "ekspansi brace" penuh.
Isaac
3
{a..c}1 {a..c}2 {a..c}3

Perluasan brace di {a..c}{1..3}diperluas dari kiri ke kanan, jadi Anda pertama mendapatkan a{1..3} b{1..3} c{1..3}dan kemudian huruf digabungkan dengan angka-angka ke a1 a2 a3 b1 b2 b3 c1 c2 c3. Untuk mendapatkan pesanan yang Anda inginkan, Anda harus menggunakan ekspresi yang sedikit lebih panjang di atas.

Kusalananda
sumber
Jika Anda ingin melakukannya untuk sejumlah besar "angka" itu tidak praktis lagi.
RUBEN GONÇALO MOROUÇO
3
@ RUBENGONÇALOMOROUÇO Tidak, dan jika Anda melakukannya untuk sejumlah besar angka, saya akan menyarankan menggunakan pendekatan alternatif, seperti loop ganda. Itu akan bekerja untuk ribuan kombinasi, sedangkan penjepit memperluas pemicu saya "daftar argumen terlalu lama" dalam konteks tertentu.
Kusalananda
2

Menggunakan loop:

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

Ini akan mengulang melalui ekspansi pertama Anda dan kemudian memperluas setiap karakter dengan yang kedua.

Jika Anda membutuhkan output semua dalam satu baris, Anda dapat menghapus \n:

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

Ini tidak akan memberi Anda trailing newline tetapi jika Anda meneruskannya ke perintah atau variabel yang seharusnya tidak menjadi masalah.

Jesse_b
sumber
1
Mengapa begitu banyak pilihan untuk solusi loop?
Isaac
Duh saya pasti salah baca. Diperbarui
Jesse_b
2

Ini berfungsi untuk kasing Anda yang sederhana dan dapat diperpanjang, tetapi akan cepat hilang kendali. Kasus-kasus yang lebih kompleks yang tidak bisa digunakan untuk membuatnya mudah dibuat.

Membalik urutan ekspansi brace, lalu menukar karakter:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'
Dijeda sampai pemberitahuan lebih lanjut.
sumber
@muru: Ups. Tetap. Terima kasih.
Dijeda sampai pemberitahuan lebih lanjut.
@ Isaac: Saya memperbaiki ruang trailing.
Dijeda sampai pemberitahuan lebih lanjut.
0

Salah satu metode sederhana adalah dengan menggunakan sort (1.2.1.2 berarti Anda mengambil satu karakter di posisi kedua dan berakhir di tempat yang sama).

$ 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

Jika Anda menginginkannya dalam satu baris, Anda dapat menggunakan tr seperti:

$ 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
Melompat Kelinci
sumber
-2

Dilakukan dengan metode di bawah ini

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

keluaran

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