Was wäre der beste Weg, um die Anzeigebreite (mindestens auf einem Terminal, auf dem Zeichen im aktuellen Gebietsschema mit der richtigen Breite angezeigt werden) einer Zeichenfolge aus einem Shell-Skript zu ermitteln?
Mich interessiert in erster Linie die Breite von Nicht-Steuerzeichen, aber auch Lösungen, die Steuerzeichen wie Rücktaste, Wagenrücklauf und horizontale Tabellierung berücksichtigen, sind willkommen.
Mit anderen Worten, ich suche nach einer Shell- API für die wcswidth()
POSIX-Funktion.
Dieser Befehl sollte zurückgeben:
$ 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
Man könnte ksh93
's verwenden printf '%<n>Ls'
, das die Zeichenbreite für das Auffüllen von <n>
Spalten berücksichtigt , oder den col
Befehl (zum Beispiel printf '++%s\b\b--\n' <character> | col -b
), um dies abzuleiten. Es gibt perl
mindestens ein Text :: CharWidth- Modul, aber es gibt direktere oder portablere Ansätze.
Das ist mehr oder weniger eine Fortsetzung der anderen Frage, bei der es um die Anzeige von Text auf der rechten Seite des Bildschirms ging, für die Sie diese Informationen benötigen würden, bevor Sie den Text anzeigen können.
quelle
Antworten:
In einem Terminal-Emulator könnte man den Cursor-Positionsbericht verwenden, um Vorher / Nachher-Positionen abzurufen, z. B. von
und finden Sie heraus, wie breit die auf dem Terminal gedruckten Zeichen sind. Da dies eine ECMA-48-Steuersequenz (sowie eine VT100-Steuersequenz) ist, die von nahezu jedem Terminal unterstützt wird, das Sie wahrscheinlich verwenden, ist sie ziemlich portabel.
Als Referenz
Letztendlich bestimmt der Terminalemulator die druckbare Breite aufgrund der folgenden Faktoren:
wcswidth
allein sagt nicht aus, wie das Kombinieren von Zeichen gehandhabt wird; POSIX erwähnt diesen Aspekt in der Beschreibung dieser Funktion nicht.wcswidth
allein untergräbt (siehe z. B. Kapitel 2. Einrichten von Cygwin ).xterm
Beispielsweise ist die Auswahl von Zeichen mit doppelter Breite für Konfigurationen vorgesehen, die hierfür erforderlich sind.Der Aufruf von Shell-APIs
wcswidth
wird in unterschiedlichem Maße unterstützt:Diese sind mehr oder weniger direkt:
wcswidth
Im Fall von Perl simulieren , C-Laufzeit von Ruby und Python aufrufen. Sie könnten sogar Flüche verwenden, z. B. aus Python (die das Kombinieren von Zeichen handhaben würden):filter
Funktion (für einzelne Zeilen)addstr
, überprüfen Sie ihn auf Fehler (falls er zu lang ist) und dann auf die Endpositionendwin
(was a nicht tun sollterefresh
)Die Verwendung von Flüchen für die Ausgabe (anstatt die Informationen an ein Skript zurückzugeben oder direkt aufzurufen
tput
) würde die gesamte Zeile löschen (filter
beschränkt sie jedoch auf eine Zeile).quelle
wcswidth()
zu irgendetwas zu sagen ist.plink
, das sich einstelltTERM=xterm
, obwohl es auf keine Steuerungssequenz reagiert. Aber ich benutze keine sehr exotischen Terminals.fold
ist anscheinend darauf ausgelegt, Mehrbyte- und Zeichen mit erweiterter Breite zu verarbeiten . So sollte es mit der Rücktaste umgehen: Die aktuelle Anzahl der Zeilenbreiten soll um eins verringert werden, obwohl die Anzahl niemals negativ werden soll. Das Dienstprogramm fold darf ein <newline> nicht unmittelbar vor oder nach einem <backspace> einfügen, es sei denn, das folgende Zeichen hat eine Breite von mehr als 1 und würde dazu führen, dass die Linienbreite die Breite überschreitet. vielleichtfold -w[num]
undpr +[num]
könnte irgendwie zusammengeschlossen werden?Für einzeilige Strings verfügt die GNU-Implementierung von
wc
über eine-L
(aka--max-line-length
) -Option, die genau das tut, wonach Sie suchen (mit Ausnahme der Steuerzeichen).quelle
tab
(setzt Tabulatorstopps alle 8 Spalten voraus).wc -L <<< 'unix'
→ 8,wc -L <<< 'Stéphane'
→ 8 undwc -L <<< 'もで 諤奯ゞ'
→ 11. PS Sie betrachten „Stéphane“ als neun Zeichen, von denen eines die Breite Null hat? Es sieht für mich aus wie acht Zeichen, von denen eines aus mehreren Bytes besteht.In my
.profile
rufe ich ein Skript auf, um die Breite eines Strings in einem Terminal zu bestimmen. Ich verwende dies, wenn ich mich an der Konsole eines Computers anmelde, auf dem ich der Systemgruppe nicht vertraueLC_CTYPE
, oder wenn ich mich remote anmelde und nicht vertraueLC_CTYPE
, dass es mit der Remote-Seite übereinstimmt. Mein Skript fragt das Terminal ab, anstatt eine Bibliothek aufzurufen, denn das war der springende Punkt in meinem Anwendungsfall: Ermitteln Sie die Codierung des Terminals.Das ist in mehrfacher Hinsicht fragil:
plink
Methode von einem Linux-Computer auf Remote-Dateien zu , und ich habe es mithilfe derplinkx
Methode gelöst .)Dies kann Ihrem Anwendungsfall entsprechen oder nicht.
Das Skript gibt die Breite in ihrem Rückgabestatus zurück, der auf 100 begrenzt ist.
quelle
printf "\r%*s\r" $((${#text}+8)) " ";
am Ende von etwas schöner gemachtcleanup
(Hinzufügen von 8 ist willkürlich; es muss lang genug sein, um die breitere Ausgabe älterer Gebietsschemata abzudecken, aber schmal genug, um einen Zeilenumbruch zu vermeiden). Dies macht den Test unsichtbar, setzt jedoch voraus, dass in der Zeile nichts gedruckt wurde (was in a in Ordnung ist~/.profile
)text="Éé"
und dann${#text}
werden Ihnen die Anzeigebreite (ich4
in einem Nicht-Unicode - Terminal und2
in einem Unicode-kompatibelen Terminal). Dies gilt nicht für Bash.${#text}
gibt Ihnen nicht die Anzeigebreite. Hier sehen Sie die Anzahl der Zeichen in der vom aktuellen Gebietsschema verwendeten Codierung. Was für meinen Zweck unbrauchbar ist, da ich die Codierung des Terminals bestimmen möchte. Dies ist nützlich, wenn Sie die Anzeigebreite aus einem anderen Grund benötigen, sie jedoch nicht genau ist, da nicht jedes Zeichen eine Einheit breit ist. Zum Beispiel haben kombinierte Akzente eine Breite von 0 und chinesische Ideogramme eine Breite von 2.Eric Pruitt hat eine beeindruckende Implementierung von
wcwidth()
undwcswidth()
in Awk geschrieben, die unter wcwidth.awk verfügbar ist . Es bietet hauptsächlich 4 Funktionenwo
wcscolumns()
auch nicht druckbare Zeichen toleriert werden.Ich habe ein Problem mit der Behandlung von Tabulatoren geöffnet, da
wcscolumns($'My sign is\t鼠鼠')
diese größer als 14 sein sollten. Update: Eric hat die Funktionwcsexpand()
zum Erweitern von Tabulatoren zu Leerzeichen hinzugefügt :quelle
Um die Hinweise auf mögliche Lösungen mit
col
undksh93
in meiner Frage zu erweitern:Verwenden von
col
frombsdmainutils
on Debian (funktioniert möglicherweise nicht mit anderencol
Implementierungen), um die Breite eines einzelnen Nicht-Steuerzeichens zu erhalten:Beispiel:
Erweitert um einen String:
Verwenden von
ksh93
'sprintf '%Ls'
:Verwenden von
perl
'sText::CharWidth
:quelle