Mengisi spasi spasi putih di string dengan karakter lain

12

Saya ingin menampilkan hello worldlebih dari 20 karakter.

printf "%-20s :\n\n" 'hello world!!'

# Actual output
hello world!!        :

# Wanted output
hello world!!========:

Namun, saya tidak ingin menyelesaikan spasi tetapi dengan " = " sebagai gantinya. Bagaimana aku melakukan itu?

smarber
sumber

Jawaban:

9

Jawaban yang diperbarui menjadi solusi yang lebih umum. lihat juga jawaban saya yang lain di bawah ini hanya menggunakan ekspansi brace shell dan pritnf.

$ str='Hello World!'
$ sed -r ':loop; s/ (=*):$/\1=:/; t loop' <<< "$(printf '%-20s:\n' "$str" )"
Hello World!========:

Bagaimana itu bekerja?

ini (=*):$/menangkap satu ruang, satu atau lebih =yang diikuti oleh titik dua :di akhir inputnya; kami menjadikan set =sebagai pertandingan grup dan \1akan menjadi referensi kembali.

Dengan :loopkami mendefinisikan label bernama loopdan dengan t loopitu akan melompat ke label itu ketika s/ (=*):$/\1=:/telah melakukan penggantian yang sukses;

Di bagian pengganti dengan \1=:, itu akan selalu menambah jumlah =s dan kembali usus besar itu sendiri ke akhir string.

αғsнιη
sumber
1
jadi Anda harus menyesuaikan bendera tergantung pada berapa banyak kata yang dimiliki input?
user1686
@ kegembiraan Saya telah memperbarui jawaban saya untuk solusi umum sekarang.
αғsнιη
20
filler='===================='
string='foo'

printf '%s\n' "$string${filler:${#string}}"

Memberi

foo=================

${#string}adalah panjang dari nilai $string, dan ${filler:${#string}}merupakan substring dari $fillerdari offset ${#string}dan seterusnya.

Lebar total output akan menjadi lebar maksimum $filleratau $string.

String pengisi dapat, pada sistem yang telah jot, dibuat secara dinamis menggunakan

filler=$( jot -s '' -c 16 '=' '=' )

(untuk 16 =baris). Sistem GNU dapat menggunakan seq:

filler=$( seq -s '=' 1 16 | tr -dc '=' )

Sistem lain mungkin menggunakan Perl atau cara lain yang lebih cepat untuk membuat string secara dinamis.

Kusalananda
sumber
mengapa tidak menggunakan printfuntuk menghasilkan filter yang hampir tersedia di semua sistem dan ekspansi penjepit dengan shell seperti bash/szh?
αғsнιη
@ sddgob Bagaimana Anda melakukannya dengan printf+ ekspansi brace bash?
Kusalananda
silakan periksa unix.stackexchange.com/a/399059/72456
αғsнιη
17
printf "%.20s:\n\n" "$str========================="

di mana %.20sformat pemotongan string

Joao
sumber
2
Ini adalah IMHO, solusi yang lebih baik daripada milikku. Hanya perlu penjelasan singkat tentang format string.
Kusalananda
12

Salah satu cara untuk melakukannya:

printf "====================:\r%s\n\n" 'hello world!!'
Satō Katsura
sumber
6
Ha! Itu trik yang cerdas! Namun, itu benar-benar akan mencetak ====================\rhello world, yang mungkin menjadi masalah jika OP perlu menyimpan ini dan tidak hanya mencetaknya ke layar.
terdon
juga echo -e '=================\rHello World!!', tetapi memiliki masalah yang sama dengan @terdon menunjuk itu.
αғsнιη
1
@ αғsнιη Hanya jika echomendukung -e. printfhampir selalu lebih baik daripada echo, karena berbagai alasan.
Satō Katsura
5

Pendekatan Perl:

$ perl -le '$k="hello world!!"; while(length($k)<20){$k.="=";} print "$k\n"'
hello world!!=======

Atau, lebih baik, @SatoKatsura tunjukkan dalam komentar:

perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Jika Anda perlu mendukung karakter multi-byte UTF, gunakan:

PERL_UNICODE='AS' perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Gagasan yang sama di shell:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"
terdon
sumber
Anda tidak perlu loop: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Namun, ini (dan semua solusi lain yang diposting sejauh ini) rusak jika karakter multi-byte terlibat.
Satō Katsura
@ SatōKatsura ooh, ya, itu rapi! Seharusnya memikirkan itu, terima kasih. Dan ya, saya berpikir untuk menambahkan disclaimer untuk kemungkinan kegagalan pada karakter multi-byte UTF, tetapi mengira itu akan menjadi komplikasi yang tidak perlu dalam konteks ini.
terdon
Saya pikir perl6mungkin memiliki cara untuk melakukannya dengan benar bahkan dengan karakter multi-byte. Tetapi di sisi lain perl6menjengkelkan dalam banyak hal.
Satō Katsura
@ SatōKatsura yah, untuk hal sederhana semacam ini, seharusnya cukup dengan hanya mengatur PERL_UNICODE='AS'. Misalnya: printf '%s' nóóös | perl -nle 'print length($_)'mencetak 8 ("salah") saat printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'mencetak 5 ("benar").
terdon
5

Cara lain adalah dengan hanya menggunakan printfperintah dan menghasilkan pola karakter padding pertama dengan Shell Brace Expansion(Anda dapat mengakhiri dengan angka ≥ area format yang ingin Anda cetak {1..end}) dan dapatkan hanya setiap karakter pertama %.1syang =s dan kemudian hanya mencetak panjang 20 karakter pertama daerah itu %.20s. Ini semacam cara yang lebih baik untuk mengulangi karakter / kata daripada menduplikasinya.

printf '%.20s:\n' "$str$(printf '%.1s' ={1..20})"
Hello World!!=======:

Penjelasan:

Biasanya sebagai Brace Expansion , shell mengembang {1..20}sebagai berikut jika kita mencetaknya.

printf '%s ' {1..20}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

Jadi dengan menambahkan tanda sama dengan itu ={1..20}, shell akan berkembang sebagai berikut.

printf '%s ' ={1..20}
=1 =2 =3 =4 =5 =6 =7 =8 =9 =10 =11 =12 =13 =14 =15 =16 =17 =18 =19 =20 

Dan dengan printf '%.1s'yang sebenarnya berarti printf '%WIDE.LENGTH', kami mencetak hanya satu PANJANG dari yang di atas dengan 1 WIDE default . jadi akan menghasilkan =s hanya dan 20 kali diulang sendiri.

Sekarang dengan printf '%.20s:\n'kita mencetak hanya 20 panjang $strdan jika panjang $str<20, sisanya akan mengambil dari yang dihasilkan =untuk mengisi bukan spasi.

αғsнιη
sumber