Wenn ich das folgende einfache Skript ausführe:
#!/bin/bash
printf "%-20s %s\n" "Früchte und Gemüse" "foo"
printf "%-20s %s\n" "Milchprodukte" "bar"
printf "%-20s %s\n" "12345678901234567890" "baz"
Es druckt:
Früchte und Gemüse foo
Milchprodukte bar
12345678901234567890 baz
Das heißt, Text mit Umlauten (z. B. ü
) wird um ein Zeichen pro Umlaut "verkleinert".
Sicher habe ich irgendwo eine falsche Einstellung, aber ich kann nicht herausfinden, welche das sein könnte.
Dies tritt auf, wenn die Codierung der Datei UTF-8 ist.
Wenn ich die Codierung in Latin-1 ändere, ist die Ausrichtung korrekt, aber die Umlaute werden falsch gerendert:
Fr�chte und Gem�se foo
Milchprodukte bar
12345678901234567890 baz
echo Früchte und Gemüse | wc -c -m
den Unterschied.printf
ist.Antworten:
POSIX verlangt
printf
, dass%-20s
diese 20 in Bytes und nicht in Zeichen gezählt werden , obwohl dies wenig sinnvollprintf
ist, da Text formatiert gedruckt wird (siehe Diskussion bei der Austin Group (POSIX) undbash
Mailinglisten).Die
printf
eingebautenbash
und die meisten anderen POSIX-Shells berücksichtigen dies .zsh
Ignoriert diese dumme Anforderung (auch in dersh
Emulation),printf
funktioniert also so, wie Sie es dort erwarten würden. Gleiches gilt für dasprintf
Builtin vonfish
(keine POSIX-ähnliche Shell).Das
ü
in UTF-8 codierte Zeichen (U + 00FC) besteht aus zwei Bytes (0xc3 und 0xbc), was die Diskrepanz erklärt.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
oderfish
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
:In
bash
:ksh93
hat eine%Ls
Formatspezifikation zum Zählen der Breite in Bezug auf die Anzeigebreite .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 derroff
Ausgabe, in derX
(fettX
) geschrieben ist alsX\bX
), obwohlksh93
alle Steuerzeichen eine Breite von haben-1
.Als weitere Optionen könnten Sie versuchen:
Das funktioniert bei einigen
expand
Implementierungen (allerdings nicht bei GNUs).Auf GNU-Systemen könnten Sie GNU verwenden,
awk
dessenprintf
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):Wenn die Ausgabe an ein Terminal gesendet wird, können Sie auch Escape-Sequenzen für die Cursorpositionierung verwenden. Mögen:
quelle
ü
caracter kann als zusammengesetzt sein ,u
+¨
, das 3 Byte. Im Fall der Frage wird sie als 2 Zeichen codiert, aber nicht alleü
werden gleich erstellt.u\u308
besteht aus zwei Zeichen (wc -m
mindestens in Unix / sense) für eine Glyphe / Graphem / Graphem-Cluster und ist bereits erwähnt und in dieser Antwort enthalten.printf(3)
(wenig Sinn nach dieser C99-Anforderung, die Sie erwähnen, danke dafür), aber nicht dasprintf(1)
Dienstprogramm, da jeder Shell-Operator oder anderes Textdienstprogramm sich mit Zeichen befasst (oder geändert wurde, um sich auch mit Zeichen zu befassen) wiewc
das bekam ein-m
(während Byte-c
blieb ) oder das bekam ein nach könnte etwas anderes als Bytes bedeuten).cut
-b
-c
Eigentlich nein, aber Ihr Terminal spricht kein lateinisch-1, und Sie erhalten daher eher Junk als Umlaute.
Sie können dies beheben, indem Sie iconv verwenden:
(Oder führen Sie einfach das gesamte Shell-Skript aus, das in iconv eingebunden ist.)
quelle