En bash, ¿cómo puedo convertir un punto de código Unicode [0-9A-F] en un carácter imprimible?


23

Tengo una lista de puntos de código Unicode, pero no conozco una forma "simple" de convertir estos valores hexadecimales en los caracteres reales que representan ...

He oído que zsh tiene echo -e '\u0965', pero yo uso Bash 4.1.

¿Hay algo tan simple como el método zsh, para bash?


Respuestas:


16

Puede usar echo de bash o / bin / echo de GNU coreutils en combinación con iconv:

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

Por defecto, iconv se convierte a su codificación local. Quizás más portátil que confiar en un shell específico o comando echo es Perl. La mayoría de los sistemas UNIX que conozco tienen Perl disponible e incluso tienen varios puertos de Windows.

perl -C -e 'print chr 0x0965'

La mayoría de las veces cuando necesito hacer esto, estoy en un editor como Vim / GVim que tiene soporte incorporado. Mientras está en modo de inserción, presione Ctrl-V seguido de u, luego escriba cuatro caracteres hexadecimales. Si quieres un personaje más allá de U + FFFF, usa una U mayúscula y escribe 8 caracteres hexadecimales. Vim también admite mapas de teclas personalizados fáciles de hacer. Convierte una serie de caracteres en otro símbolo. Por ejemplo, tengo un mapa de teclas que desarrollé llamado www, convierte TM a ™, (C) a ©, (R) a ®, y así sucesivamente. También tengo un mapa de teclas para Klingon para cuando sea necesario. Estoy seguro de que Emacs tiene algo similar. Si está en una aplicación GTK + que incluye GVim y GNOME Terminal, puede probar Control-Shift-u seguido de 4 caracteres hexadecimales para crear un personaje Unicode. Estoy seguro de que KDE / Qt tiene algo similar.

ACTUALIZACIÓN: A partir de Bash 4.2, parece ser una característica incorporada ahora:

echo $'\u0965'

ACTUALIZACIÓN: Además, hoy en día un ejemplo de Python probablemente sería preferible a Perl. Esto funciona en Python 2 y 3:

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

Gracias ... el perl es agradable y conciso, pero me tiene un poco desconcertado sobre cómo sabe tratar el valor como UTF-16BE ... Supongo que eso es lo que significa "chr" ...
Peter.O

@fred ese es un buen punto. El ejemplo de Perl es sensible a la configuración regional. El -C permite el procesamiento completo de Unicode, pero el ejemplo funciona porque mi entorno local utiliza un ejemplo de Unicode. Si configuro LANG en C, recibo una advertencia sobre un carácter ancho en la impresión, pero aún se imprime. Si imprimo chr 0xa2en un entorno local UTF-8 obtengo un signo de centavos ¢, pero si uso LANG = C, obtengo porque imprime el byte 0xa2 que no es válido en UTF-8. El ejemplo de Vim / GVim es semi-sensible a la configuración regional. Más correctamente, a la codificación del archivo. Si inició Vim en un entorno local que no sea UTF-8, deberá hacerlo:set encoding=utf-8
penguin359

@fred Debo señalar que Perl trata el valor de chr como un punto de código Unicode si Perl se inicia en un entorno Unicode como UTF-8. Un punto de código es el número único que representa un carácter y no está vinculado a ninguna codificación, como UTF-16BE o UTF-8. Lo convierte a la codificación correcta cuando lo imprime. Por ejemplo, el signo cuneiforme A es el punto de código U + 012000. Puedo usarlo chr 0x12000en Perl (suponiendo que Unicode esté activo) para representarlo. En UTF-16BE, esto es 0xd8, 0x08, 0xdc y 0x00. Tu personaje es U + 0965, que son los bytes 0x09 seguidos de 0x65 en UTF-16BE.
penguin359

@ penguin359 ... Gracias, un día (con suerte) echaré un buen vistazo a Perl ... Parece indescifrablemente críptico, pero también lo hizo sed y regex, inicialmente, y ahora es bastante fácil ... tal vez sea un poco como vim; una curva de aprendizaje empinada, luego navegar ... Es bueno leer su explicación ... allana el camino ...
Peter.O

Acabo de (re) descubrir que la impresión de Steven D no manejará el bloque ASCII del rango Unicode, por lo que su perlrespuesta ahora es la mejor (para mis requisitos particulares). Anteriormente descarté printf (hace meses) , pero me había olvidado de eso. Aquí está la pregunta / respuesta sobre sus límites ... ¿Por qué printf informa un error en todos menos tres puntos de código Unicode (rango ASCII)
Peter.O

13

Bash 4.2 (lanzado en 2011) añadido soporte para echo -e '\u0965', printf '\u0965', printf %b '\u0965'y echo $'\u0965'también trabajo.

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

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

Gracias ... Todavía estoy usando bash 4.1.5 en Ubuntu 10.04, pero es bueno saber que ahora está disponible en 4.2. (+1)
Peter

1
+1; tenga en cuenta que las bash 4.2.xversiones tienen un error en el que los valores entre 0x80y 0xff( 128 - 255), es decir, en el rango ASCII extendido, NO están correctamente codificados en UTF8 y en su lugar simplemente se pasan, lo que da como resultado un carácter UTF8 no válido que algunos terminales representan ?. A partir de (al menos) 4.3.11esto se ha solucionado; si se echo $'\ued'procesa í, entonces el error no está presente.
mklement0

5

Si tienes GNU coreutils, prueba printf:

$ printf '\u0965\n'

echo puede hacer el trabajo si su consola está usando UTF-8 y tiene la codificación UTF-8:

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

Puede encontrar una tabla de codificaciones hexadecimales Unicode a UTF-8 aquí: http://www.utf8-chartable.de/ . Puede convertir los puntos de código Unicode a hexadecimal utilizando varios lenguajes de secuencias de comandos. Aquí hay un ejemplo usando Python:

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

El siguiente es un script de Perl que convertirá los argumentos al valor hexadecimal correcto (muchos paréntesis innecesarios aquí):

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

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

Por ejemplo,

./uni2utf 0965
e0a5a5

Por supuesto, si tiene Perl o Python, también podría usarlos para imprimir los caracteres.


Gracias ... echoNo harán lo que quiero, ya que los Codepoints son Big-Endian UTF-16 de 2 bytes ... ¡pero me han recordado que hay 2 funciones printf! (Pensé que printf podría hacerlo, y parece que estaba invocando el incorrecto) ... $(which printf)funciona ... Gracias por el ejemplo de Python ... pero por esto (mi curva de aprendizaje), estoy tratando de mantenerme lo más cerca posible como sea posible "bash" como el único lenguaje de escrutinio involucrado ... (cuando me sienta lo suficientemente cómodo con bash, me quedaré atrapado en Python ... por cierto, .encode('hex')está un paso más allá de lo que necesito ... (pensé que parecía un poco ocupado allí :)
Peter.O

Sí, el .encode ('hex') era solo para obtener el código hexadecimal que parecía funcionar con echo para mí. Me alegra que al menos parte de esto haya sido útil.
Steven D

Te acabo de ver un fragmento de Perl ... gracias ... es bueno tener estas diversas soluciones presentadas ... La printf es exactamente lo que estaba buscando (un solo comando, según el ejemplo de zsh) ... .. Puedo publicar mi método de no usar otro lenguaje de script que funciona en una secuencia de datos hexadecimales (no \ u, etc.) ..
Peter.O

Particularmente me gusta la brevedad de lo printfanterior, pero no maneja los valores por debajo de `` \ 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` la solución se ve bastante bien ahora :) .. Es una invocación única, y yo después de "fácil de escribir", así que daré él el verde-tick paraperl
Peter.O

2

ACTUALIZACIÓN: Aquí hay una manera bash de hacer un único valor Unicode ... (por "bash" quiero decir: no usar ningún otro lenguaje de secuencias de comandos) ... gracias a Gilles por su sugerencia en este Q / A de askubuntu .
De acuerdo con este enlace : recode (Obsoletes iconv, dos2unix, unix2dos) .. Editar: pero según el comentario a continuación, "obsoletos" puede significar "alternativa"

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

Aquí hay un método para procesar un volcado hexadecimal sin procesar como entrada (es decir, sin prefijos con escape como; \ u0965 y no \ x09 \ x65) ...
xxdes una utilidad de volcado hexadecimal (empaquetado con vim-common) que puede revertir un volcado hexadecimal sin procesar para los caracteres que representa el volcado ... Los puntos de código Unicode son UTF-16BigEndian, que es exactamente lo que es un volcado hexadecimal ...
xxden modo reversible acepta una secuencia de valores hexadecimales con saltos de línea que se ignoran.

Este script crea una secuencia UTF-16BE, que luego vuelve a los caracteres originales.
La última línea contiene los dos comandos necesarios; xxdyiconv

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

Aquí está la salida (que muestra la entrada de volcado hexadecimal UTF-16BE, primero).
Nota; xxdsegmenta su propia salida con una nueva línea en 60 dígitos hexadecimales ... La opción de reversión ignora estas nuevas líneas ... ignora cualquiera / todas las nuevas líneas (ya que no son dígitos hexadecimales) ...

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

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

Como parece que usó la información de penguin359 en su respuesta, podría considerar marcar su respuesta como correcta en lugar de la mía.
Steven D

@ Steven D: un comentario digno de mención, pero "parecer" es la palabra clave. He estado usando iconv como este durante un par de días, lo que me hizo preguntarme si hay un solo comando. He hecho un procesamiento similar de todo el archivo en Windows (C ++), por lo que tengo una comprensión razonable de Unicode. Realmente buscaba un bashmétodo rápido y simple . Por "bash" quiero decir: usar el lenguaje de script bash; no python / perl desde dentro de bash). He agregado esto como respuesta porque puede ser de algún valor para alguien que lea esta página. Es una buena frase para un archivo completo. Tu printfes la mejor respuesta para mí.
Peter.O

2
No diría que recode obsolescentes iconv, de hecho, recode es más antiguo que iconv, y actualmente iconv se instala mucho más comúnmente por defecto que recode (por ejemplo, en Linux, iconv casi siempre se instala porque viene con libc).
Gilles 'SO- deja de ser malvado'

Gracias ... Me preguntaba sobre eso ... Esa página web no es exactamente la referencia definitiva ... así que es más una alternativa ...
Peter.O

1

Suponiendo que la codificación predeterminada para su sistema operativo es UTF-8 (cierto para la mayoría de las distribuciones actuales), puede usar bash directamente para convertir cualquier punto de código UNICODE:

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

Por supuesto, el glifo aparecerá correctamente solo si tiene la fuente correcta. A partir de bash 4.3, todos los puntos de código funcionarán correctamente. Y estas dos opciones integradas también funcionarán:

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

Tenga en cuenta que para bash 4.2, los puntos de código Unicode de 0x80a 0xFFestán codificados incorrectamente (error de bash). Para solucionar este problema, debe echar un vistazo al programa en este sitio (también es bueno para profundizar en el tema de la conversión de números a caracteres).


Funciona para mí en bash 4.3 y zsh. ¿Existe un informe de error para bash 4.2 que pueda vincular?
Mikel

esto me parece el error correcto: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlDescripción: \ uy \ U codifican incorrectamente valores entre \ u80 y \ uff

0

Usando la sustitución de patrones en bash versión 4.2 (y superior):

${parameter/pattern/string}

como se describe aquí 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
Tenga en cuenta que, como se indicó en una respuesta anterior , esto solo funciona en la versión 4.2 de bash (y superior). De hecho, esto agrega bastante poco a la respuesta anterior.
G-Man dice 'reinstalar a Monica' el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.