POSIX verlangt printf
, dass %-20s
diese 20 in Bytes und nicht in Zeichen gezählt werden , obwohl dies wenig sinnvoll printf
ist, da Text formatiert gedruckt wird (siehe Diskussion bei der Austin Group (POSIX) und bash
Mailinglisten).
Die printf
eingebauten bash
und die meisten anderen POSIX-Shells berücksichtigen dies .
zsh
Ignoriert diese dumme Anforderung (auch in der sh
Emulation), printf
funktioniert also so, wie Sie es dort erwarten würden. Gleiches gilt für das printf
Builtin von fish
(keine POSIX-ähnliche Shell).
Das ü
in UTF-8 codierte Zeichen (U + 00FC) besteht aus zwei Bytes (0xc3 und 0xbc), was die Diskrepanz erklärt.
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
Diese Zeichenfolge besteht aus 18 Zeichen, ist 18 Spalten breit ( -L
eine GNU- wc
Erweiterung, die die Anzeigebreite der breitesten Zeile in der Eingabe angibt), ist jedoch in 20 Bytes codiert.
In zsh
oder fish
würde der Text korrekt ausgerichtet.
Es gibt jetzt auch Zeichen mit der Breite 0 (wie das Kombinieren von Zeichen wie U + 0308, das Kombinieren von Diaresis) oder mit der doppelten Breite wie in vielen asiatischen Skripten (ganz zu schweigen von Steuerzeichen wie Tabulator) und die sogar zsh
nicht ausgerichtet werden würden die richtig.
Beispiel in zsh
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
In bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
hat eine %Ls
Formatspezifikation zum Zählen der Breite in Bezug auf die Anzeigebreite .
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
Das funktioniert immer noch nicht, wenn der Text Steuerzeichen wie TAB enthält (wie könnte es sein, printf
müsste wissen, wie weit die Tabstopps im Ausgabegerät voneinander entfernt sind und an welcher Position der Druck beginnt). Es funktioniert aus Versehen mit Backspace-Zeichen (wie in der roff
Ausgabe, in der X
(fett X
) geschrieben ist als X\bX
), obwohl ksh93
alle Steuerzeichen eine Breite von haben -1
.
Als weitere Optionen könnten Sie versuchen:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
Das funktioniert bei einigen expand
Implementierungen (allerdings nicht bei GNUs).
Auf GNU-Systemen könnten Sie GNU verwenden, awk
dessen printf
Anzahl in Zeichen angegeben ist (keine Bytes, keine Anzeigebreiten, also immer noch nicht OK für die Zeichen mit 0 oder 2 Breiten, aber OK für Ihr Beispiel):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Wenn die Ausgabe an ein Terminal gesendet wird, können Sie auch Escape-Sequenzen für die Cursorpositionierung verwenden. Mögen:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"