Jika saya menjalankan skrip sederhana berikut:
#!/bin/bash
printf "%-20s %s\n" "Früchte und Gemüse" "foo"
printf "%-20s %s\n" "Milchprodukte" "bar"
printf "%-20s %s\n" "12345678901234567890" "baz"
Mencetak:
Früchte und Gemüse foo
Milchprodukte bar
12345678901234567890 baz
yaitu, teks dengan umlaut (seperti ü
) adalah "menyusut" oleh satu karakter per umlaut.
Tentu saja, saya memiliki beberapa pengaturan yang salah di suatu tempat, tetapi saya tidak dapat menemukan yang mana.
Ini terjadi jika penyandian file adalah UTF-8.
Jika saya mengubah penyandiannya ke latin-1, perataannya benar, tetapi umlaut yang ditampilkan salah:
Fr�chte und Gem�se foo
Milchprodukte bar
12345678901234567890 baz
echo Früchte und Gemüse | wc -c -m
perbedaannya.printf
adalah.Jawaban:
POSIX membutuhkan
printf
's%-20s
untuk menghitung orang-orang 20 dalam hal byte tidak karakter meskipun itu masuk akal sebagaiprintf
adalah untuk mencetak teks , diformat (lihat diskusi di Austin Grup (POSIX) danbash
mailing list).Kerangka
printf
bawaanbash
dan sebagian besar kerang POSIX lainnya menghormatinya.zsh
mengabaikan persyaratan konyol (bahkan dalamsh
persaingan) sehinggaprintf
berfungsi seperti yang Anda harapkan di sana. Sama untukprintf
builtin darifish
(bukan shell seperti POSIX).The
ü
karakter (U + 00FC), ketika dikodekan dalam UTF-8 terbuat dari dua byte (0xc3 dan 0xbc), yang menjelaskan perbedaan tersebut.String itu terdiri dari 18 karakter, lebar 18 kolom (
-L
menjadiwc
ekstensi GNU untuk melaporkan lebar tampilan garis terluas dalam input) tetapi dikodekan pada 20 byte.Di
zsh
ataufish
, teks akan disejajarkan dengan benar.Sekarang, ada juga karakter yang memiliki 0-lebar (seperti menggabungkan karakter seperti U + 0308, yang menggabungkan diaresis) atau memiliki lebar ganda seperti di banyak skrip Asiatik (belum lagi karakter kontrol seperti Tab) dan bahkan
zsh
tidak akan menyelaraskan mereka dengan benar.Contoh, di
zsh
:Dalam
bash
:ksh93
memiliki%Ls
spesifikasi format untuk menghitung lebar dalam hal tampilan lebar.Itu masih tidak berfungsi jika teks berisi karakter kontrol seperti TAB (bagaimana mungkin?
printf
Harus tahu seberapa jauh jarak tab berhenti di perangkat output dan di mana ia mulai mencetak). Ia bekerja secara tidak sengaja dengan karakter backspace (seperti dalamroff
output di manaX
(dicetak tebalX
) ditulisX\bX
) meskipunksh93
menganggap semua karakter kontrol memiliki lebar-1
.Sebagai opsi lain, Anda dapat mencoba:
Itu bekerja dengan beberapa
expand
implementasi (bukan GNU sekalipun).Pada sistem GNU, Anda bisa menggunakan GNU
awk
yangprintf
menghitung dalam karakter (bukan byte, bukan lebar layar, jadi masih tidak OK untuk karakter 0 lebar atau 2 lebar, tapi OK untuk sampel Anda):Jika output masuk ke terminal, Anda juga dapat menggunakan urutan pelarian posisi kursor. Suka:
sumber
ü
caracter dapat disusun sebagaiu
+¨
, yang merupakan 3 byte. Dalam kasus pertanyaan, ini dikodekan sebagai 2 karakter, tetapi tidak semuaü
dibuat sama.u\u308
adalah dua karakter (wc -m
setidaknya dalam Unix / sense) untuk satu glyph / graphem / graphem-cluster dan sudah disebutkan dan dimasukkan dalam jawaban ini.printf(3)
(sedikit masuk akal setelah persyaratan C99 yang Anda sebutkan, terima kasih untuk itu), tetapi bukanprintf(1)
utilitas karena setiap operator shell atau utilitas teks lainnya berurusan dengan karakter (atau dimodifikasi untuk juga berurusan dengan karakter sepertiwc
yang mendapat-m
(sementara byte-c
tetap ) atau yang mendapat after bisa berarti sesuatu yang lain daripada byte).cut
-b
-c
Sebenarnya, tidak, tetapi terminal Anda tidak berbicara bahasa latin-1, dan karena itu Anda mendapatkan sampah daripada umlaut.
Anda dapat memperbaikinya dengan menggunakan ikonv:
(atau jalankan saja skrip shell yang disalurkan ke iconv)
sumber