En bash, comment puis-je convertir un point de code Unicode [0-9A-F] en un caractère imprimable?


23

J'ai une liste de points de code Unicode, mais je ne connais pas de moyen "simple" de convertir ces valeurs hexadécimales en caractères réels qu'elles représentent ...

J'ai entendu dire que zsh l' a fait echo -e '\u0965', mais j'utilise bash 4.1.

Existe-t-il quelque chose d'aussi simple que la méthode zsh, pour bash?


Réponses:


16

Vous pouvez utiliser l'écho de bash ou / bin / echo de GNU coreutils en combinaison avec iconv:

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

Par défaut, iconv se convertit en votre encodage local. Perl est peut-être plus portable que de s'appuyer sur un shell spécifique ou une commande echo. La plupart des systèmes UNIX que je connais ont Perl disponible et même plusieurs ports Windows.

perl -C -e 'print chr 0x0965'

La plupart du temps, lorsque je dois faire cela, je suis dans un éditeur comme Vim / GVim qui a un support intégré. En mode insertion, appuyez sur Ctrl-V suivi de u, puis tapez quatre caractères hexadécimaux. Si vous voulez un caractère au-delà de U + FFFF, utilisez un U majuscule et tapez 8 caractères hexadécimaux. Vim prend également en charge des keymaps personnalisés faciles à créer. Il convertit une série de caractères en un autre symbole. Par exemple, j'ai une carte-clé que j'ai développée, appelée www, elle convertit TM en ™, (C) en ©, (R) en ®, etc. J'ai également une carte-clé pour Klingon lorsque cela devient nécessaire. Je suis sûr qu'Emacs a quelque chose de similaire. Si vous êtes dans une application GTK + qui comprend GVim et GNOME Terminal, vous pouvez essayer Control-Shift-u suivi de 4 caractères hexadécimaux pour créer un caractère Unicode. Je suis sûr que KDE / Qt a quelque chose de similaire.

MISE À JOUR: À partir de Bash 4.2, il semble être une fonctionnalité intégrée maintenant:

echo $'\u0965'

MISE À JOUR: De plus, de nos jours, un exemple Python serait probablement préféré à Perl. Cela fonctionne à la fois en Python 2 et 3:

python -c 'print(u"\u0965")'

Merci ... le Perl est agréable et concis, mais il me laisse un peu perplexe sur la façon dont il sait traiter la valeur comme UTF-16BE .. Je suppose que c'est ce que signifie le "chr" ...
Peter.O

@fred c'est un bon point. L'exemple Perl est sensible aux paramètres régionaux. Le -C active le traitement Unicode complet, mais l'exemple fonctionne car mes paramètres régionaux utilisent un exemple Unicode. Si je mets LANG sur C, j'obtiens un avertissement concernant un caractère large en impression, mais il s'imprime toujours. Si j'imprime chr 0xa2dans un environnement local UTF-8, j'obtiens un signe cents ¢, mais si j'utilise LANG = C, j'obtiens car il imprime l'octet 0xa2 qui n'est pas valide en UTF-8. L'exemple Vim / GVim est semi-sensible aux paramètres régionaux. Plus correctement, au codage du fichier. Si vous avez démarré Vim dans un environnement local non UTF-8, vous devrez:set encoding=utf-8
penguin359

@fred Je dois souligner que Perl traite la valeur de chr comme un point de code Unicode si Perl est démarré dans un environnement Unicode comme UTF-8. Un point de code est le numéro unique qui représente un caractère et n'est lié à aucun codage tel que UTF-16BE ou UTF-8. Il le convertit en l'encodage correct lorsqu'il l'imprime. Par exemple, le signe cunéiforme A est le point de code U + 012000. Je peux utiliser chr 0x12000en Perl (en supposant qu'Unicode est actif) pour le représenter. Dans UTF-16BE, il s'agit de 0xd8, 0x08, 0xdc et 0x00. Votre caractère est U + 0965 qui se trouve être juste les octets 0x09 suivis de 0x65 dans UTF-16BE.
penguin359

@ penguin359 .. Merci, un jour (avec un peu de chance) j'aurai un bon aperçu de perl .. Cela semble insondable cryptique, mais ensuite sed et regex aussi, au début, et maintenant c'est assez facile ... peut-être que c'est un peu comme vim; une courbe d'apprentissage abrupte, puis une navigation simple .... C'est bon de lire votre explication ... cela ouvre la voie ..
Peter.O

Je viens de (re) découvrir que printf soultion de Steven D ne gérera pas le bloc ASCII de la gamme unicode, donc votre perlréponse est maintenant la meilleure (pour mes besoins particuliers) .. J'avais précédemment exclu printf (il y a des mois) , mais je l'avais oublié. Voici le queston / réponse sur ses limites ... Pourquoi printf signale une erreur sur tous les points de
code

13

Bash 4.2 (publié en 2011) a ajouté la touche pour echo -e '\u0965', printf '\u0965', printf %b '\u0965'et echo $'\u0965'aussi le travail.

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

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.

Merci ... J'utilise toujours bash 4.1.5 dans Ubuntu 10.04, mais il est certainement bon de savoir qu'il est désormais disponible en 4.2. (+1)
Peter.O

1
+1; notez que les bash 4.2.xversions ont un bogue où les valeurs entre 0x80et 0xff( 128 - 255) - c'est-à-dire dans la plage ASCII étendue - ne sont PAS correctement codées en UTF8 et sont simplement transmises, ce qui entraîne un caractère UTF8 non valide que certains terminaux rendent ?. Depuis (au moins), 4.3.11cela a été corrigé; si echo $'\ued's'affiche í, le bogue n'est pas présent.
mklement0

5

Si vous avez des coreutils GNU, essayez printf:

$ printf '\u0965\n'

echo peut faire le travail si votre console utilise UTF-8 et que vous avez le codage UTF-8:

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

Vous pouvez trouver un tableau des codages hexadécimaux Unicode vers UTF-8 ici: http://www.utf8-chartable.de/ . Vous pouvez convertir les points de code Unicode en hexadécimal à l'aide d'un certain nombre de langages de script. Voici un exemple utilisant python:

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

Ce qui suit est un script Perl qui convertira les arguments en la valeur hexadécimale correcte (de nombreuses parenthèses inutiles ici):

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

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

Par exemple,

./uni2utf 0965
e0a5a5

Bien sûr, si vous avez Perl ou Python, vous pouvez également les utiliser pour imprimer les caractères.


Merci .. Le echone fera pas ce que je veux, car les Codepoints sont des Big-Endian UTF-16 à 2 octets .. mais vous m'avez rappelé qu'il y a 2 fonctions printf! (Je pensais que printf pouvait le faire, et il semble que j'invoque le mauvais) ... $(which printf)fonctionne ... Merci pour l'exemple python .. mais pour cela (ma courbe d'apprentissage), j'essaie de rester aussi proche que possible de "bash" comme le seul langage d'écriture impliqué .. (quand je serai assez à l'aise avec bash, je resterai coincé dans Python ... btw, .encode('hex')c'est un pas au-delà de ce dont j'ai besoin .. (je pensais que ça avait l'air un peu occupé là-dedans :)
Peter.O

Ouais, le .encode ('hex') était juste pour obtenir le code hexadécimal qui semblait fonctionner avec l'écho pour moi. Heureux qu'au moins une partie de cela ait été utile.
Steven D

Je viens de vous voir un extrait de perl .. merci ... c'est bien d'avoir ces différentes solutions déposées ... La printf est exactement ce que je cherchais (une seule commande, selon l'exemple zsh) ... .. Je pourrais bien poster ma méthode de non-utilisation d'un autre langage de script qui fonctionne sur un flux de données hexadécimales (no \ u, etc.) ..
Peter.O

J'aime particulièrement la brièveté de ce qui printfprécède, mais il ne gère pas les valeurs inférieures à la ... 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* solution `` \ u00A0 perl` semble assez bien maintenant :) .. C'est un seul appel, et après "facile à taper", je vais donc donner lui la coche verte pourperl
Peter.O

2

MISE À JOUR: Voici une façon bash de faire une seule valeur Unicode ... (par "bash" je veux dire: ne pas utiliser un autre langage de script) .. merci à Gilles pour une suggestion dans ce Q / R askubuntu .
Selon ce lien : recode (Obsoletes iconv, dos2unix, unix2dos) .. Edit: mais selon le commentaire ci-dessous, "obsoletes" peut simplement signifier "alternative"

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

Voici une méthode pour traiter un vidage hexadécimal brut en entrée (c.-à-d. Aucun préfixe d'échappement comme; \ u0965 et aucun \ x09 \ x65) ..
xxdest un utilitaire de vidage hexadécimal (fourni avec vim-common) qui peut annuler un vidage hexadécimal brut aux caractères que le vidage représente ... Les points de code Unicode sont UTF-16BigEndian, ce qui est exactement ce qu'est un vidage hexadécimal ..
xxden mode retour accepte un flux de valeurs hexadécimales avec des sauts de ligne qui sont ignorés.

Ce script crée un flux UTF-16BE, qu'il rétablit ensuite aux caractères d'origine.
La dernière ligne contient les deux commandes nécessaires; xxdeticonv

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

Voici la sortie (montrant l'entrée de vidage hexadécimal UTF-16BE, en premier).
Remarque; xxdsegmente sa propre sortie avec une nouvelle ligne à 60 chiffres hexadécimaux ... L'option de retour ignore ces nouvelles lignes .. elle ignore toutes les nouvelles lignes (car ce ne sont pas des chiffres hexadécimaux) ..

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

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!

Comme il semble que vous ayez utilisé les informations de penguin359 dans votre réponse, vous pourriez envisager de marquer sa réponse comme correcte plutôt que la mienne.
Steven D

@Steven D: un commentaire remarquable, mais "sembler" est le mot clé. J'utilise iconv comme ça depuis quelques jours maintenant, ce qui m'a fait me demander s'il n'y a qu'une seule commande. J'ai fait un traitement complet de fichiers similaires dans Windows (C ++), j'ai donc une compréhension raisonnable d'Unicode. J'étais vraiment à la recherche d'une bashméthode simple et rapide . Par «bash», j'entends: utiliser le langage de script bash; pas python / perl depuis bash). J'ai ajouté ceci comme réponse car cela peut être d'une certaine valeur pour quelqu'un qui lit cette page. C'est une bonne doublure pour un fichier entier. Votre printfréponse est la meilleure pour moi.
Peter.O

2
Je ne dirais pas que recode obsolète iconv, en fait recode est plus ancien que iconv, et de nos jours iconv est beaucoup plus couramment installé par défaut que recode (par exemple, sous Linux, iconv est presque toujours installé car il est livré avec libc).
Gilles 'SO- arrête d'être méchant'

Merci .. Je me
posais des

1

En supposant que l'encodage par défaut de votre système d'exploitation est UTF-8 (vrai pour la plupart des distributions actuelles), vous pouvez utiliser directement bash pour convertir n'importe quel point de code UNICODE:

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

Bien sûr, le glyphe n'apparaîtra correctement que si vous avez la bonne police. Depuis bash 4.3, tous les points de code fonctionneront correctement. Et ces deux options intégrées fonctionneront également:

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

Notez que pour bash 4.2, le code Unicode pointe de 0x80à 0xFFn'est pas encodé correctement (bug bash). Pour contourner ce problème, vous devez jeter un œil au programme sur ce site (également bon pour un examen approfondi du problème de la conversion des nombres en caractères.


Fonctionne pour moi dans bash 4.3 et zsh. Y a-t-il un rapport de bogue pour bash 4.2 auquel vous pouvez vous lier?
Mikel

cela me semble être le bug correct: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlDescription: \ u et \ U encodent incorrectement des valeurs entre \ u80 et \ uff

0

Utilisation de la substitution de modèle dans bash version 4.2 (et versions supérieures):

${parameter/pattern/string}

comme décrit ici 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"}
λ         

1
Notez que, comme indiqué dans une réponse précédente , cela ne fonctionne que dans la version bash 4.2 (et supérieure). En fait, cela ajoute assez peu à la réponse précédente.
G-Man dit `` Réintègre Monica ''
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.