POSIX wymaga printf
, %-20s
aby policzyć te 20 w kategoriach bajtów, a nie znaków, chociaż nie ma to większego sensu, jak printf
drukowanie tekstu , formatowanie (patrz dyskusja w Austin Group (POSIX) i bash
listy mailingowe).
Uwzględniają to printf
wbudowane bash
i większość innych powłok POSIX.
zsh
ignoruje to głupie wymaganie (nawet w sh
emulacji), więc printf
działa tak, jak można się tam spodziewać. To samo dotyczy printf
wbudowanej fish
(nie powłoki podobnej do POSIX).
ü
Znak (U + 00FC), w którym zakodowane UTF-8 składa się z dwóch bajtów (0xc3 i 0xbc), co wyjaśnia różnicę.
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
Łańcuch ten składa się z 18 znaków, ma 18 kolumn szerokości ( -L
jest wc
rozszerzeniem GNU raportującym szerokość wyświetlania najszerszej linii na wejściu), ale jest zakodowany na 20 bajtach.
W zsh
lub fish
tekst zostałby wyrównany poprawnie.
Teraz są też znaki, które mają szerokość 0 (jak łączenie znaków, takich jak U + 0308, łączenie diurezy) lub mają podwójną szerokość, jak w wielu skryptach azjatyckich (nie wspominając o znakach kontrolnych, takich jak Tab), a nawet zsh
nie wyrównywałyby te poprawnie.
Przykład w zsh
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
W bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
ma %Ls
specyfikację formatu, aby policzyć szerokość pod względem szerokości wyświetlania .
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
To nadal nie działa, jeśli tekst zawiera znaki sterujące, takie jak TAB (jak to możliwe? printf
Musiałby wiedzieć, jak daleko od siebie są tabulatory w urządzeniu wyjściowym i w jakiej pozycji zaczyna drukować). Działa przypadkowo ze znakami backspace (jak na roff
wyjściu, gdzie zapisano X
(pogrubienie X
) jako X\bX
), chociaż ksh93
uważa, że wszystkie znaki sterujące mają szerokość -1
.
Jako inne opcje możesz spróbować:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
Działa to z niektórymi expand
implementacjami (choć nie GNU).
W systemach GNU możesz użyć GNU, awk
którego printf
liczenie jest w znakach (nie bajtach, nie szerokościach wyświetlania, więc nadal nie jest OK dla znaków o szerokości 0 lub 2 szerokości, ale OK dla twojej próbki):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Jeśli dane wyjściowe trafiają do terminala, możesz także użyć sekwencji ucieczki pozycjonowania kursora. Lubić:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"