Wie kann ich in bash einen Unicode-Codepoint [0-9A-F] in ein druckbares Zeichen konvertieren?

23

Ich habe eine Liste von Unicode-Codepunkten, aber ich kenne keine "einfache" Möglichkeit, diese Hex-Werte in die tatsächlichen Zeichen zu konvertieren, die sie darstellen ...

Ich habe gehört, dass zsh hat echo -e '\u0965', aber ich benutze Bash 4.1.

Gibt es etwas so Einfaches wie die zsh-Methode für Bash?

Peter.O
quelle
1
Siehe auch StackOverflow: Konvertieren von Unicode nach UTF-8 mit den Konsolenwerkzeugen in * nix
hippietrail 18.02.14

Antworten:

16

Sie können bashs Echo oder / bin / echo von GNU coreutils in Kombination mit iconv verwenden:

echo -ne '\x09\x65' | iconv -f utf-16be

Standardmäßig konvertiert iconv in Ihre Gebietsschemacodierung. Möglicherweise ist Perl portabler als die Verwendung eines bestimmten Shell- oder Echo-Befehls. Die meisten mir bekannten UNIX-Systeme verfügen über Perl und sogar über mehrere Windows-Ports.

perl -C -e 'print chr 0x0965'

Die meiste Zeit, wenn ich das tun muss, bin ich in einem Editor wie Vim / GVim, der integrierte Unterstützung hat. Drücken Sie im Einfügemodus Strg-V, gefolgt von u, und geben Sie dann vier Hexadezimalzeichen ein. Wenn Sie ein Zeichen jenseits von U + FFFF möchten, verwenden Sie ein U in Großbuchstaben und geben Sie 8 hexadezimale Zeichen ein. Vim unterstützt auch benutzerdefinierte, einfach zu erstellende Keymaps. Es konvertiert eine Reihe von Zeichen in ein anderes Symbol. Ich habe zum Beispiel eine von mir entwickelte Keymap namens www, die TM in ™, (C) in ©, (R) in ® und so weiter konvertiert. Ich habe auch eine Keymap für Klingonisch für den Fall, dass dies notwendig wird. Ich bin sicher, Emacs hat etwas Ähnliches. Wenn Sie sich in einer GTK + -Anwendung befinden, die GVim und GNOME Terminal enthält, können Sie Control-Shift-u gefolgt von 4 Hex-Zeichen verwenden, um ein Unicode-Zeichen zu erstellen. Ich bin sicher, KDE / Qt hat etwas Ähnliches.

UPDATE: Ab Bash 4.2 scheint es ein eingebautes Feature zu sein:

echo $'\u0965'

UPDATE: Heutzutage wird wahrscheinlich ein Python-Beispiel Perl vorgezogen. Dies funktioniert sowohl in Python 2 als auch in 3:

python -c 'print(u"\u0965")'
penguin359
quelle
Danke ... das Perl ist nett und knapp, aber es hat mich ein bisschen verwirrt, wie man den Wert als UTF-16BE behandelt. Ich denke, das ist, was "chr" bedeutet ...
Peter.O
@fred das ist ein guter Punkt. Das Perl-Beispiel ist vom Gebietsschema abhängig. Das -C aktiviert die vollständige Unicode-Verarbeitung, aber das Beispiel funktioniert, weil mein Gebietsschema ein Unicode-Beispiel verwendet. Wenn ich LANG auf C setze, wird eine Warnung über ein breites Zeichen im Ausdruck angezeigt, das jedoch weiterhin gedruckt wird. Wenn ich chr 0xa2in einem UTF-8-Gebietsschema drucke, bekomme ich ein Cent-Zeichen ¢, aber wenn ich LANG = C verwende, bekomme ich , weil es das in UTF-8 ungültige Byte 0xa2 druckt. Das Vim / GVim-Beispiel ist für das Gebietsschema semi-sensitiv. Genauer gesagt zur Dateicodierung. Wenn Sie Vim in einem Nicht-UTF-8-Gebietsschema gestartet haben, müssen Sie:set encoding=utf-8
penguin359 am
@fred Ich sollte darauf hinweisen, dass Perl den Wert von chr als Unicode-Codepunkt behandelt, wenn Perl in einem Unicode-Gebietsschema wie UTF-8 gestartet wird. Ein Codepoint ist die eindeutige Nummer, die ein Zeichen darstellt und nicht mit einer Codierung wie UTF-16BE oder UTF-8 verknüpft ist. Es konvertiert es in die richtige Kodierung, wenn es ausgedruckt wird. Beispielsweise ist das Keilschriftzeichen A der Codepunkt U + 012000. Ich kann chr 0x12000in Perl (vorausgesetzt, Unicode ist aktiv) verwenden, um es darzustellen. In UTF-16BE sind dies 0xd8, 0x08, 0xdc und 0x00. Ihr Zeichen ist U + 0965, was zufällig die Bytes 0x09 gefolgt von 0x65 in UTF-16BE sind.
Pinguin359
@ penguin359 .. Danke, eines Tages (hoffentlich) werde ich mir Perl genauer ansehen. Es scheint unfassbar kryptisch zu sein, aber anfangs ist es Sed und Regex auch so, und jetzt ist es ganz einfach ... vielleicht ist es ein bisschen wie vim; Eine steile Lernkurve, dann einfaches Segeln ... Es ist gut, Ihre Erklärung zu lesen ... es ebnet den Weg ...
Peter.O
Ich habe gerade (erneut) entdeckt, dass Steven Ds printf-Lösung den ASCII-Block des Unicode-Bereichs nicht verarbeiten kann, daher ist Ihre perlAntwort jetzt die beste (für meine speziellen Anforderungen). Ich hatte printf zuvor ausgeschlossen (vor Monaten). , aber ich hatte es vergessen. Hier ist die Frage / Antwort über ihre Grenzen ... Warum meldet printf einen Fehler mit Ausnahme von drei (ASCII-Bereich) Unicode-Codepunkten
Peter.O
13

Bash 4.2 (veröffentlicht im Jahre 2011) Unterstützung für echo -e '\u0965', printf '\u0965', printf %b '\u0965'und echo $'\u0965'auch Arbeit.

http://tiswww.case.edu/php/chet/bash/FAQ :

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.
Lri
quelle
Danke ... Ich verwende immer noch hauptsächlich Bash 4.1.5 in Ubuntu 10.04, aber es ist sicherlich gut zu wissen, dass es jetzt in 4.2 verfügbar ist. (+1)
Peter.O
1
+1; Beachten Sie, dass bash 4.2.xVersionen einen Fehler aufweisen, bei dem Werte zwischen 0x80und 0xff( 128 - 255) - dh im erweiterten ASCII-Bereich - NICHT korrekt UTF8-codiert und stattdessen nur durchgereicht werden, was zu einem ungültigen UTF8-Zeichen führt, das einige Terminals als darstellen ?. Ab (mindestens) wurde 4.3.11dies behoben; Wird echo $'\ued'gerendert í, ist der Fehler nicht vorhanden.
mklement0
5

Wenn Sie GNU Coreutils haben, versuchen Sie printf:

$ printf '\u0965\n'

echo kann die Aufgabe erledigen, wenn Ihre Konsole UTF-8 verwendet und Sie die UTF-8-Codierung haben:

$ echo -e '\xE0\xA5\xA5'

Eine Tabelle mit Unicode-zu-UTF-8-Hex-Codierungen finden Sie hier: http://www.utf8-chartable.de/ . Sie können die Unicode-Codepunkte mit einer Reihe von Skriptsprachen in Hex konvertieren. Hier ist ein Beispiel mit Python:

python -c "print(unichr(int('0965', 16)).encode('utf-8').encode('hex'))"

Das folgende Perl-Skript konvertiert Argumente in den richtigen Hexadezimalwert (viele unnötige Klammern hier):

#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Encode;

foreach (@ARGV) {
    say unpack('H*', encode('utf8', chr(hex($_))))
}

Zum Beispiel,

./uni2utf 0965
e0a5a5

Wenn Sie Perl oder Python haben, können Sie diese natürlich auch zum Drucken der Zeichen verwenden.

Steven D
quelle
Danke .. Die machen echonicht was ich will, da die Codepoints 2-Byte-UTF-16-Big-Endian sind .. aber du hast mich daran erinnert, dass es 2 printf-Funktionen gibt! (Ich dachte printf könnte es tun und es scheint, als würde ich das falsche aufrufen) ... $(which printf)funktioniert ... Danke für das Python-Beispiel ... aber für dieses (meine Lernkurve) versuche ich, so nah wie möglich zu bleiben Möglichst "bash" als einzige Scriptsprache. (Wenn ich mit bash vertraut bin, werde ich in Python stecken bleiben. Übrigens .encode('hex')ist ein Schritt weiter, als ich brauche.) (Ich dachte, es sah so aus ein bisschen beschäftigt dort :)
Peter.O
Ja, der .encode ('hex') war nur, um den Hex-Code zu erhalten, der für mich mit Echo zu funktionieren schien. Ich bin froh, dass zumindest ein Teil davon hilfreich war.
Steven D
Ich habe gerade gesehen, dass Sie ein Perl-Snippet haben. Danke. Es ist gut, diese verschiedenen Lösungen zur Verfügung zu haben. Das printf ist genau das, wonach ich gesucht habe (ein einziger Befehl, wie im zsh-Beispiel). .. Ich kann meine Methode, die keine andere Skriptsprache verwendet, veröffentlichen , die mit einem Stream von Hex-Daten (no \ u, etc)
funktioniert
Ich mag besonders die Kürze des printfObigen, aber es behandelt keine Werte unter `` \ u00A0 ... I've just re-discovered something I already knew (but dropped off the radar)... Here is a Question I asked about 4 months ago; [Why does printf report an error on all but three (ASCII-range) Unicode Codepoints](http://askubuntu.com/questions/20806/why-does-printf-report-an-error-on-all-but-three-ascii-range-unicode-codepoints)... So *penguin359's* perl` Lösung sieht jetzt ziemlich gut aus :) .. Es ist eine einzelne Aufforderung, und ich nach "einfach zu tippen", also gebe ich ihm das grüne Häkchen fürperl
Peter.O
2

UPDATE: Hier ist ein bash Weise einen einzelnen Unicode - Wert zu tun ... (durch "bash" Ich meine: nicht ein andere Skriptsprache) .. dank Gilles für eine suggeston in diesem askubuntu Q / A .
Laut diesem Link : recode (obsoletes iconv, dos2unix, unix2dos) .. Edit: aber laut dem Kommentar unten, kann "obsoletes" nur "alternative" bedeuten

      echo -n 0x0965 |recode UTF-16BE/x4..UTF-8

Hier ist eine Methode, um einen rohen Hex-Dump als Eingabe zu verarbeiten (dh keine Escape-Präfixe wie; \ u0965 und no \ x09 \ x65) ..
xxdist ein Hex-Dump-Dienstprogramm ( gepackt mit vim-common), mit dem ein roher Hex-Dump zurückgesetzt werden kann für die Zeichen, die der Dump darstellt ... Unicode-Codepoints sind UTF-16BigEndian, was genau das ist, was ein Hex-Dump ist.
xxdIm Revert-Modus wird ein Stream von Hex-Werten mit Zeilenumbrüchen akzeptiert, die ignoriert werden.

Dieses Skript erstellt einen UTF-16BE-Stream, der dann auf die ursprünglichen Zeichen zurückgesetzt wird.
Die letzte Zeile enthält die beiden benötigten Befehle. xxdundiconv

for line in \
  "Matsuo Basho (1644-1694)" \
  "  pond" \
  "  frog jumps in" \
  "  plop!"
do 
  echo "$line" |iconv -f "$(locale charmap)" -t "UTF-16BE" |xxd -ps -u 
done |
#    (---this is the **revert** code---) 
tee >(xxd -p -u -r |iconv -f "UTF-16BE") ;echo

Hier ist der Ausgang (der zuerst den UTF-16BE-Hex-Dump-Eingang zeigt).
Hinweis; xxdsegmentiert seine eigene Ausgabe mit einer Newline mit 60 Hex-Ziffern ... Die Option revert ignoriert diese Newlines. Es werden alle Newlines ignoriert (da es sich nicht um Hex-Ziffern handelt).

004D0061007400730075006F00200042006100730068006F002000280031
003600340034002D00310036003900340029000A
002000200070006F006E0064000A
0020002000660072006F00670020006A0075006D0070007300200069006E
000A
002000200070006C006F00700021000A

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!
Peter.O
quelle
Da Sie in Ihrer Antwort anscheinend die Informationen von penguin359 verwendet haben, sollten Sie in Betracht ziehen, seine Antwort als richtig und nicht als meine zu markieren.
Steven D
@Steven D: Ein bemerkenswerter Kommentar, aber "scheinen" ist das maßgebliche Wort. Ich benutze iconv jetzt schon seit ein paar Tagen und habe mich gefragt, ob es einen einzigen Befehl gibt. Ich habe eine ähnliche vollständige Dateiverarbeitung in Windows (C ++) durchgeführt, sodass ich ein vernünftiges Verständnis von Unicode habe. Ich war wirklich nach einer schnellen und einfachen bashMethode. Mit "Bash" meine ich: Verwenden der Bash-Skriptsprache; nicht Python / Perl aus Bash). Ich habe dies als Antwort hinzugefügt, da es für jemanden, der diese Seite liest, von Nutzen sein kann. Es ist ein guter Einzeiler für eine ganze Datei. Du printfbist die beste Antwort für mich.
Peter.O
2
Ich würde nicht sagen, dass recode obsoletes iconv ist, in der Tat ist recode älter als iconv, und heutzutage wird iconv viel häufiger als recode installiert (zum Beispiel wird iconv unter Linux fast immer installiert, weil es mit libc geliefert wird).
Gilles 'SO- hör auf böse zu sein'
Danke. Ich habe mich darüber
gewundert
1

Unter der Annahme, dass die Standardcodierung für Ihr Betriebssystem UTF-8 ist (gilt für die meisten aktuellen Distributionen), können Sie bash direkt zum Konvertieren von UNICODE-Codepunkten verwenden:

echo -e "Unicode Character 'DEVANAGARI DOUBLE DANDA' (U+0965) \U0965"

Natürlich wird die Glyphe nur dann richtig angezeigt, wenn Sie die richtige Schriftart haben. Ab Bash 4.3 funktionieren alle Codepunkte korrekt. Und diese beiden eingebauten Optionen funktionieren auch:

printf "%b" "Unicode Character (U+0965) \U0965 \n"
echo $'Unicode Character (U+0965) \U0965'

Beachten Sie, dass für Bash 4.2 die Unicode-Codepunkte von 0x80bis 0xFFfalsch codiert sind (Bash-Fehler). Um dieses Problem zu umgehen, müssen Sie sich das Programm auf dieser Site ansehen (auch für einen tieferen Einblick in das Problem der Konvertierung von Zahlen in Zeichen geeignet).

HalosGhost
quelle
Funktioniert bei mir in bash 4.3 und zsh. Gibt es einen Fehlerbericht für Bash 4.2, auf den Sie verlinken können?
Mikel
Das sieht für mich nach dem richtigen Bug aus: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlBeschreibung: \ u und \ u kodieren falsch Werte zwischen \ u80 und \ uff
0

Verwenden der Mustersubstitution in der Bash-Version 4.2 (und höher):

${parameter/pattern/string}

wie hier beschrieben http://steve-parker.org/sh/tips/pattern-substitution/

UNICODE_HEX="U+02211"
printf ${UNICODE_HEX/U+/"\U"}


UNICODE_HEX="U+03BB"
printf ${UNICODE_HEX/U+/"\U"}
λ         
illuzent
quelle
1
Beachten Sie, dass dies, wie in einer vorherigen Antwort angegeben , nur in der Bash-Version 4.2 (und höher) funktioniert. Tatsächlich fügt dies der früheren Antwort ziemlich wenig hinzu.
G-Man sagt, dass Monica