Apa yang paling dekat dengan cara portabel untuk mendapatkan lebar layar (setidaknya pada terminal (yang menampilkan karakter dalam lokal saat ini dengan lebar yang benar)) dari serangkaian karakter dari skrip shell.
Saya terutama tertarik pada lebar karakter non-kontrol tetapi solusi yang memperhitungkan karakter kontrol akun seperti backspace, carriage return, tabulasi horizontal juga diterima.
Dengan kata lain, saya mencari shell API di sekitar wcswidth()
fungsi POSIX.
Perintah itu harus kembali:
$ that-command 'unix' # 4 fullwidth characters
8
$ that-command 'Stéphane' # 9 characters, one of which zero-width
8
$ that-command 'もで 諤奯ゞ' # 5 double-width Japanese characters and a space
11
Satu bisa menggunakan ksh93
's printf '%<n>Ls'
yang memperhitungkan lebar karakter untuk padding untuk <n>
kolom, atau col
perintah (dengan misalnya printf '++%s\b\b--\n' <character> | col -b
) untuk mencoba dan mendapatkan itu, ada Teks :: CharWidth perl
modul setidaknya, tetapi ada pendekatan yang lebih langsung atau portabel.
Itu kurang lebih merupakan tindak lanjut dari pertanyaan lain yang tentang menampilkan teks di sebelah kanan layar yang Anda perlu memiliki informasi itu sebelum menampilkan teks.
sumber
Jawaban:
Dalam emulator terminal, seseorang dapat menggunakan laporan posisi kursor untuk mendapatkan posisi sebelum / sesudah, misalnya dari
dan temukan seberapa lebar karakter yang dicetak di terminal. Karena itu adalah urutan kontrol ECMA-48 (dan juga VT100) yang didukung oleh hampir semua terminal yang mungkin Anda gunakan, ini cukup portabel.
Sebagai referensi
Pada akhirnya, terminal emulator menentukan lebar yang dapat dicetak, karena faktor-faktor ini:
wcswidth
sendiri tidak memberitahu bagaimana menggabungkan karakter ditangani; POSIX tidak menyebutkan aspek ini dalam deskripsi fungsi itu.wcswidth
sendiri (lihat misalnya Bab 2. Menyiapkan Cygwin ).xterm
misalnya memiliki ketentuan untuk memilih karakter lebar ganda untuk konfigurasi yang diperlukan ini.Panggilan Shell API
wcswidth
didukung ke berbagai tingkatan:Itu kurang lebih langsung: simulasi
wcswidth
dalam kasus Perl, memanggil runtime C dari Ruby dan Python. Anda bahkan bisa menggunakan kutukan, misalnya, dari Python (yang akan menangani penggabungan karakter):filter
fungsi (untuk satu baris)addstr
, memeriksa kesalahan (jika terlalu lama), dan kemudian untuk posisi akhirendwin
(yang seharusnya tidak melakukan arefresh
)Menggunakan kutukan untuk output (daripada memberi makan informasi kembali ke skrip atau menelepon langsung
tput
) akan menghapus seluruh baris (filter
tidak membatasi ke baris).sumber
wcswidth()
harus dikatakan tentang apa pun.plink
, yang menetapkanTERM=xterm
meskipun tidak menanggapi urutan kontrol apa pun. Tapi saya tidak menggunakan terminal yang sangat eksotis.fold
tampaknya dispesifikasikan untuk menangani karakter multi-byte dan extended width . Begini caranya menangani backspace: Hitungan lebar garis saat ini harus dikurangi oleh satu, meskipun hitungan tidak akan pernah menjadi negatif. Utilitas lipat tidak boleh memasukkan <newline> segera sebelum atau setelah <backspasi> apa pun, kecuali karakter berikut memiliki lebar lebih besar dari 1 dan akan menyebabkan lebar garis melebihi lebar. mungkinfold -w[num]
danpr +[num]
bisa digabungkan entah bagaimana?Untuk string satu-baris, implementasi GNU
wc
memiliki opsi-L
(alias--max-line-length
) yang melakukan apa yang Anda cari (kecuali karakter kontrol).sumber
tab
juga (mengasumsikan tab berhenti setiap 8 kolom).wc -L <<< 'unix'
→ 8,wc -L <<< 'Stéphane'
→ 8, danwc -L <<< 'もで 諤奯ゞ'
→ 11. PS Anda menganggap "Stéphane" sebagai sembilan karakter, salah satunya adalah lebar nol? Bagiku seperti delapan karakter, salah satunya multi-byte.Di saya
.profile
, saya memanggil skrip untuk menentukan lebar string pada terminal. Saya menggunakan ini ketika masuk pada konsol mesin di mana saya tidak mempercayai set-sistemLC_CTYPE
, atau ketika saya login jarak jauh dan tidak bisa percayaLC_CTYPE
untuk mencocokkan sisi remote. Skrip saya menanyakan terminal, daripada memanggil perpustakaan apa pun, karena itulah inti dari kasus penggunaan saya: tentukan pengkodean terminal.Ini rapuh dalam beberapa cara:
plink
metode ini, dan saya menyelesaikannya dengan menggunakanplinkx
metode itu .)Ini mungkin atau mungkin tidak cocok dengan kasus penggunaan Anda.
Script mengembalikan lebar dalam status pengembaliannya, dipangkas menjadi 100. Contoh penggunaan:
sumber
printf "\r%*s\r" $((${#text}+8)) " ";
pada akhircleanup
(menambahkan 8 adalah sewenang-wenang; perlu cukup lama untuk menutupi output yang lebih luas dari lokal yang lebih tua tetapi cukup sempit untuk menghindari pembungkus garis). Ini membuat tes tidak terlihat, meskipun juga mengasumsikan tidak ada yang dicetak pada garis (yang baik-baik saja dalam a~/.profile
)text="Éé"
dan kemudian${#text}
akan memberi Anda lebar layar (saya dapatkan4
di terminal non-unicode dan2
di terminal yang sesuai dengan unicode). Ini tidak benar untuk bash.${#text}
tidak memberi Anda lebar layar. Ini memberi Anda jumlah karakter dalam pengkodean yang digunakan oleh lokal saat ini. Yang tidak berguna untuk tujuan saya karena saya ingin menentukan pengkodean terminal. Ini berguna jika Anda menginginkan lebar layar karena alasan lain, tetapi tidak akurat karena tidak setiap karakter memiliki lebar satu unit. Misalnya, menggabungkan aksen memiliki lebar 0, dan ideogram Cina memiliki lebar 2.Eric Pruitt menulis implementasi mengesankan
wcwidth()
danwcswidth()
dalam Bahasa Inggris yang tersedia di wcwidth.awk . Ini terutama menyediakan 4 fungsidi mana
wcscolumns()
juga mentolerir karakter yang tidak dapat dicetak.Saya membuka masalah menanyakan tentang penanganan TAB karena
wcscolumns($'My sign is\t鼠鼠')
harus lebih besar dari 14. Pembaruan: Eric menambahkan fungsiwcsexpand()
untuk memperluas TAB ke spasi:sumber
Untuk memperluas petunjuk tentang kemungkinan solusi menggunakan
col
danksh93
dalam pertanyaan saya:Menggunakan
col
daribsdmainutils
pada Debian (mungkin tidak bekerja dengancol
implementasi lain ), untuk mendapatkan lebar karakter non-kontrol tunggal:Contoh:
Diperpanjang untuk string:
Menggunakan
ksh93
'sprintf '%Ls'
:Menggunakan
perl
'sText::CharWidth
:sumber