¿Cómo se hace eco de un carácter Unicode de 4 dígitos en Bash?

224

Me gustaría agregar el cráneo y las tibias cruzadas de Unicode a mi indicador de shell (específicamente el 'CRÁNEO Y CRUZES' (U + 2620)), pero no puedo entender el encantamiento mágico para hacer que el eco lo escupe, o cualquier otro, Carácter Unicode de 4 dígitos. Los de dos dígitos son fáciles. Por ejemplo, echo -e "\ x55",.

Además de las respuestas a continuación, debe tenerse en cuenta que, obviamente, su terminal debe ser compatible con Unicode para que la salida sea lo que espera. gnome-terminal hace un buen trabajo, pero no está necesariamente activado de manera predeterminada.

En la aplicación Terminal de macOS Vaya a Preferencias-> Codificaciones y elija Unicode (UTF-8).

masukomi
fuente
77
Tenga en cuenta que su comentario de "2 dígitos es fácil (hacer eco)" solo es válido para valores hasta "\x7F"en un entorno local UTF-8 (que la bashetiqueta sugiere que es el suyo) ... los patrones representados por un solo byte nunca están en el rango \x80-\xFF. Este rango es ilegal en caracteres UTF-8 de byte único. por ejemplo, un valor de punto de código Unicode de U+0080(es decir \x80) es en realidad 2 bytes en UTF-8 .. \xC2\x80..
Peter.O
44
Por ej printf "\\u007C\\u001C".
kenorb
NB: para mí gnome-terminal, echo -e '\ufc'no produce una ü, incluso con la codificación de caracteres establecida en UTF-8. Sin embargo, por ejemplo urxvt, se imprime , por ejemplo, printf "\\ub07C\\ub01C"como se esperaba (no con un o cuadro).
isomorphismes
@ Peter.O ¿Por qué la bashetiqueta es una pista tan útil? ¿Son comunes los diferentes terminales en CJK o ...?
isomorphismes
1
@ Peter.O zsh, fish, scsh, élfico, etc. Hay muchos shells diferentes, cada uno puede manejar caracteres Unicode como quieran (o no). "bash" deja en claro que esta pregunta no se trata de un caparazón extraño que hace las cosas de manera diferente.
masukomi

Respuestas:

237

En UTF-8 en realidad son 6 dígitos (o 3 bytes).

$ printf '\xE2\x98\xA0'

Para verificar cómo está codificada por la consola, use hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003
vartec
fuente
55
La mina produce " " en lugar de ☠ ... ¿Por qué es eso?
trusktr
8
Es verdad. Descubrí que estaba usando en LANG=Clugar de LANG=en_US.UTF-8. Ahora mis terminales en Gnome muestran los símbolos correctamente ... Los terminales reales (tty1-6) todavía no lo hacen.
trusktr
66
Para aquellas personas que prueban un hexdump: se 0000000 f0 9f 8d batraduce en \xf0\x9f\x8d\xba. Ejemplo eco: echo -e "\xf0\x9f\x8d\xba".
Blaise
8
También puede utilizar la $'...'sintaxis para obtener el carácter codificado en a una variable sin utilizar un $(...)subnivel de captura, para su uso en contextos que por sí mismas no interpretan las secuencias de escape:skull=$'\xE2\x98\xA0'
Andrew Janke
77
Otra cosa sobre hexdump: en mi máquina, sale el segundo comando en las respuestas 0000000 98e2 00a0. Por supuesto, el 0000000es solo un desplazamiento sin importancia, pero los bytes después de que se traducen \xe2\x98\xa0, porque la máquina usa el orden de los pequeños bytes endian.
sigalor
98
% echo -e '\u2620'     # \u takes four hexadecimal digits

% echo -e '\U0001f602' # \U takes eight hexadecimal digits
😂

Esto funciona en Zsh (he comprobado la versión 4.3) y en Bash 4.2 o posterior.

Juliano
fuente
16
eso solo escupe \ u2620 cuando lo hago.
masukomi
Para mí también. ¿Qué caparazón estás usando, Juliano?
Joachim Sauer
2
Lo siento, olvidé decir que uso zsh.
Juliano
32
Se agregó soporte para \ u en Bash 4.2.
Lri
44
NO funciona para mí, Mac OS 10.14.2, bash (GNU bash, versión 3.2.57 (1) -release (x86_64-apple-darwin18)). Simplemente imprime la entrada - $ echo -e '\ u2620' <enter> simplemente imprime: \ u2620
Motti Shneor
68

Mientras sus editores de texto puedan hacer frente a Unicode (presumiblemente codificado en UTF-8), puede ingresar el punto de código Unicode directamente.

Por ejemplo, en el editor de texto de Vim ingresarías al modo de inserción y presionarías Ctrl+ V+U y luego el número de punto de código como un número hexadecimal de 4 dígitos (almohadilla con ceros si es necesario). Entonces escribirías Ctrl+ V+ U 2 6 2 0. Ver: ¿Cuál es la forma más fácil de insertar caracteres Unicode en un documento?

En una terminal que ejecuta Bash, escribiría CTRL + SHIFT+ Uy escribiría el punto de código hexadecimal del carácter que desea. Durante la entrada, el cursor debe mostrar un subrayado u. El primer no dígito que escriba finaliza la entrada y representa el carácter. Entonces podría imprimir U + 2620 en Bash usando lo siguiente:

echo CTRL+ SHIFT+U2620ENTERENTER

(La primera entrada finaliza la entrada Unicode y la segunda ejecuta el echocomando).

Crédito: Pregúntele a Ubuntu SE

RobM
fuente
1
Una buena fuente para los puntos de código hexademico
RobM
1
La versión de vim que estoy usando (7.2.411 en RHEL 6.3) no responde como se desea cuando hay un punto entre ctrl-v y u, pero funciona bien cuando se omite ese punto.
Chris Johnson
@ChrisJohnson: eliminé el período de las instrucciones, no tenía la intención de presionar una tecla (por lo que no apareció con el efecto del teclado). Perdón por la confusion.
RobM
55
Cuidado: esto funciona en un terminal que ejecuta Bash solo si lo está ejecutando en un entorno GTK + , como Gnome.
nr
1
La capacidad C-S-u 2 6 2 0es una característica de su emulador de terminal, Método de entrada X (XIM) o similar. AFAIK, no podrá enviar ambos SHIFTy CTRLa la capa terminal. El terminal solo habla en caracteres, en lugar de en keyyms y códigos de teclas como su servidor X (también, es de 7 bits para todos los efectos). En este mundo, CTRLenmascara los 4 bits más significativos (& 0b00001111) que resultan en
nabin-info
31

Aquí hay una implementación de Bash completamente interna, sin bifurcación, tamaño ilimitado de caracteres Unicode.

fast_chr() {
    local __octal
    local __char
    printf -v __octal '%03o' $1
    printf -v __char \\$__octal
    REPLY=$__char
}

function unichr {
    local c=$1    # Ordinal of char
    local l=0    # Byte ctr
    local o=63    # Ceiling
    local p=128    # Accum. bits
    local s=''    # Output string

    (( c < 0x80 )) && { fast_chr "$c"; echo -n "$REPLY"; return; }

    while (( c > o )); do
        fast_chr $(( t = 0x80 | c & 0x3f ))
        s="$REPLY$s"
        (( c >>= 6, l++, p += o+1, o>>=1 ))
    done

    fast_chr $(( t = p | c ))
    echo -n "$REPLY$s"
}

## test harness
for (( i=0x2500; i<0x2600; i++ )); do
    unichr $i
done

La salida fue:

─━│┃┄┅┆┇┈┉┊┋┌┍┎┏
┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟
┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯
┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿
╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏
═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟
╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯
╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿
▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏
▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟
■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯
▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿
◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●
◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟
◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯
◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿
Orwellophile
fuente
Tengo mucha curiosidad por el razonamiento detrás del método circular y el uso específico de la variable REPLY. Supongo que inspeccionó la fuente de bash o la ejecutó o algo para optimizar, lo que puedo ver cómo sus opciones podrían optimizarse, aunque dependen en gran medida del intérprete).
nabin-info
14

Simplemente ponga "☠" en su script de shell. En la configuración regional correcta y en una consola habilitada para Unicode, se imprimirá bien:

$ echo 

$

Una "solución" fea sería la salida de la secuencia UTF-8, pero eso también depende de la codificación utilizada:

$ echo -e '\xE2\x98\xA0'

$
Joachim Sauer
fuente
13

One-liner rápido para convertir caracteres UTF-8 a su formato de 3 bytes:

var="$(echo -n '☠' | od -An -tx1)"; printf '\\x%s' ${var^^}; echo
David King
fuente
55
No llamaría al ejemplo anterior rápido (con 11 comandos y sus parámetros) ... Además, solo maneja caracteres de 3 bytes UTF-8 '(los caracteres UTF-8 pueden ser 1, 2 o 3 bytes) ... Esto es un poco más corto y funciona para 1-3 ++++ bytes: printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u)... xxd se envía como parte del paquete 'vim-common'
Peter.O
PD: Acabo de notar que el ejemplo anterior de hexdump / awk está cambiando la secuencia de bytes en un par de bytes. Esto no se aplica a un volcado UTF-8. Sería relevante si se tratara de un volcado de UTF-16LE y quisiera generar Unicode puntos de código , pero no tiene sentido aquí ya que la entrada es UTF-8 y la salida es exactamente como entrada (más el \ x antes de cada dígito hexadecimal) -pair)
Peter.O
77
Los caracteres UTF-8 pueden ser secuencias de 1 a 4 bytes
cms
1
basado en el comentario de @ Peter.O, encuentro lo siguiente, aunque más grande, bastante útil:hexFromGlyph(){ if [ "$1" == "-n" ]; then outputSeparator=' '; shift; else outputSeparator='\n'; fi for glyph in "$@"; do printf "\\\x%s" $(printf "$glyph"|xxd -p -c1 -u); echo -n -e "$outputSeparator"; done } # usage: $ hexFromGlyph ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF $ hexFromGlyph -n ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF
StephaneAG
2
Buen dios hombre. Considera: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ... disfruta 👍
nabin-info
8

Estoy usando esto:

$ echo -e '\u2620'

Esto es bastante más fácil que buscar una representación hexadecimal ... Estoy usando esto en mis scripts de shell. Eso funciona en gnome-term y urxvt AFAIK.

Metal3d
fuente
2
@masukomi si sabes cómo usar brew, puedes instalar un bash más reciente y usarlo. Lo anterior funciona bien en mi terminal Mac cuando uso el bash actualizado.
mcheema
Sí, eso está bien con las nuevas versiones de bash. Hower solicita cadenas, por ejemplo, $ PS1 no utiliza formatos de escape de eco
cms
6

Es posible que deba codificar el punto de código como octal para que la rápida expansión lo decodifique correctamente.

U + 2620 codificado como UTF-8 es E2 98 A0.

Entonces en Bash,

export PS1="\342\230\240"

hará que su caparazón se convierta en calavera y huesos.

cms
fuente
hola, ¿cuál es el código que debo ingresar para "e0 b6 85"? como puedo encontrarlo
Udayantha Udy Warnasuriya
solo convierta los números hexadecimales (base 16) e0 b6 85 en octal (base 8) - usar una calculadora es probablemente la forma más fácil de hacer esto
cms
e0 b6 85 hex es 340 266 205 octal
cms
Esto funcionó, muchas gracias! Y por cierto, puede encontrar la versión octal final en estas páginas: graphemica.com/%E2%9B%B5
Perlnika
6

En bash para imprimir un carácter Unicode a la salida, use \ x, \ u o \ U (primero para el hex de 2 dígitos, segundo para el hex de 4 dígitos, tercero para cualquier longitud)

echo -e '\U1f602'

Si desea asignarlo a una variable, use la sintaxis $ '...'

x=$'\U1f602'
echo $x
usuario2622016
fuente
5

Si no te importa una línea Perl:

$ perl -CS -E 'say "\x{2620}"'

-CShabilita la decodificación UTF-8 en la entrada y la codificación UTF-8 en la salida. -Eevalúa el siguiente argumento como Perl, con características modernas como sayhabilitado. Si no desea una nueva línea al final, use en printlugar de say.

Flimm
fuente
5

Cualquiera de estos tres comandos imprimirá el carácter que desee en una consola, siempre que la consola acepte caracteres UTF-8 (la mayoría de los actuales):

echo -e "SKULL AND CROSSBONES (U+2620) \U02620"
echo $'SKULL AND CROSSBONES (U+2620) \U02620'
printf "%b" "SKULL AND CROSSBONES (U+2620) \U02620\n"

SKULL AND CROSSBONES (U+2620) 

Después, puede copiar y pegar el glifo real (imagen, carácter) en cualquier editor de texto (habilitado para UTF-8).

Si necesita ver cómo se codifica dicho punto de código Unicode en UTF-8, use xxd (visor hexadecimal mucho mejor que od):

echo $'(U+2620) \U02620' | xxd
0000000: 2855 2b32 3632 3029 20e2 98a0 0a         (U+2620) ....

That means that the UTF8 encoding is: e2 98 a0

O, en HEX para evitar errores: 0xE2 0x98 0xA0. Es decir, los valores entre el espacio (HEX 20) y el avance de línea (Hex 0A).

Si desea una inmersión profunda en la conversión de números a caracteres: ¡ mire aquí para ver un artículo del wiki de Greg (BashFAQ) sobre la codificación ASCII en Bash!

usuario2350426
fuente
re: "O, en HEX para evitar errores ..." No creo que convertir un carácter unicode a alguna codificación binaria que exprese en caracteres hexadecimales, ayude a evitar errores. El uso de la notación unicode en "bash" evitaría mejor los errores, es decir: "\ uHHHH --- el carácter Unicode (ISO / IEC 10646) cuyo valor es el ---- valor hexadecimal HHHH (uno a cuatro dígitos hexadecimales); \ UHHHHHHHH ---- el carácter Unicode (ISO / IEC 10646) cuyo valor es el ---- valor hexadecimal HHHHHHHH (de uno a ocho dígitos hexadecimales)
Astara
4

El printfincorporado (al igual que el coreutils ' printf) conoce la \usecuencia de escape que acepta caracteres Unicode de 4 dígitos:

   \uHHHH Unicode (ISO/IEC 10646) character with hex value HHHH (4 digits)

Prueba con Bash 4.2.37 (1):

$ printf '\u2620\n'
Michael Jaros
fuente
printf también es un shell incorporado. Probablemente esté utilizando el macOS bash predeterminado (v3). Intente \printfusar el ejecutable independiente, o intente con bash actualizado
mcint
4

Perdón por revivir esta vieja pregunta. Pero cuando se usabash hay un enfoque muy fácil para crear puntos de código Unicode a partir de una entrada ASCII simple, que incluso no se bifurca en absoluto:

unicode() { local -n a="$1"; local c; printf -vc '\\U%08x' "$2"; printf -va "$c"; }
unicodes() { local a c; for a; do printf -vc '\\U%08x' "$a"; printf "$c"; done; };

Úselo de la siguiente manera para definir ciertos puntos de código

unicode crossbones 0x2620
echo "$crossbones"

o para volcar los primeros 65536 puntos de código unicode en stdout (toma menos de 2 segundos en mi máquina. El espacio adicional es para evitar que ciertos caracteres fluyan entre sí debido a la fuente monoespacio de shell):

for a in {0..65535}; do unicodes "$a"; printf ' '; done

o para contar una pequeña historia de padres muy típica (esto necesita Unicode 2010):

unicodes 0x1F6BC 32 43 32 0x1F62D 32 32 43 32 0x1F37C 32 61 32 0x263A 32 32 43 32 0x1F4A9 10

Explicación:

  • printf '\UXXXXXXXX' imprime cualquier carácter Unicode
  • printf '\\U%08x' numberimprime \UXXXXXXXXcon el número convertido a hexadecimal, este luego se alimenta a otroprintf para imprimir realmente el carácter Unicode
  • printf reconoce octal (0oct), hexadecimal (0xHEX) y decimal (0 o números que comienzan con 1 a 9) como números, para que pueda elegir la representación que mejor se ajuste
  • printf -v var ..reúne la salida de printfen una variable, sin tenedor (que acelera enormemente las cosas)
  • local variable está ahí para no contaminar el espacio de nombres global
  • local -n var=otheralias vara other, de modo que la asignación a varaltera other. Una parte interesante aquí es quevar es parte del espacio de nombres local, mientras que otheres parte del espacio de nombres global.
    • Tenga en cuenta que no hay tal cosa como localo globalespacio de nombres en bash. Las variables se mantienen en el entorno, y siempre son globales. Local simplemente guarda el valor actual y lo restaura cuando la función se deja de nuevo. Otras funciones llamadas desde dentro de la función localaún verán el valor "local". Este es un concepto fundamentalmente diferente de todas las reglas de alcance normales que se encuentran en otros lenguajes (y lo que bashhace es muy poderoso, pero puede generar errores si usted es un programador que no es consciente de eso).
Tino
fuente
bueno, no funciona en absoluto para mí. cualquier intento de usar cualquiera de sus funciones, emite: línea 6: local: -n: opción no válida local: uso: nombre local [= valor] ... Estoy usando la última (10.14.2) MacOS y bash (GNU bash , versión 3.2.57 (1) -release (x86_64-apple-darwin18))
Motti Shneor
4

Aquí hay una lista de todos los emoji unicode disponibles:

https://en.wikipedia.org/wiki/Emoji#Unicode_blocks

Ejemplo:

echo -e "\U1F304"
🌄

Para obtener el valor ASCII de este personaje, use hexdump

echo -e "🌄" | hexdump -C

00000000  f0 9f 8c 84 0a                                    |.....|
00000005

Y luego use los valores informados en formato hexadecimal

echo -e "\xF0\x9F\x8C\x84\x0A"
🌄
Matheus
fuente
Hacer eco de la cadena \ U <hex> no funciona en OSX, solo genera exactamente lo que está entre comillas.
masukomi
2

Fácil con una línea única Python2 / 3:

$ python -c 'print u"\u2620"'    # python2
$ python3 -c 'print(u"\u2620")'  # python3

Resultados en:

Chris Johnson
fuente
2

En Bash:

UnicodePointToUtf8()
{
    local x="$1"               # ok if '0x2620'
    x=${x/\\u/0x}              # '\u2620' -> '0x2620'
    x=${x/U+/0x}; x=${x/u+/0x} # 'U-2620' -> '0x2620'
    x=$((x)) # from hex to decimal
    local y=$x n=0
    [ $x -ge 0 ] || return 1
    while [ $y -gt 0 ]; do y=$((y>>1)); n=$((n+1)); done
    if [ $n -le 7 ]; then       # 7
        y=$x
    elif [ $n -le 11 ]; then    # 5+6
        y=" $(( ((x>> 6)&0x1F)+0xC0 )) \
            $(( (x&0x3F)+0x80 ))" 
    elif [ $n -le 16 ]; then    # 4+6+6
        y=" $(( ((x>>12)&0x0F)+0xE0 )) \
            $(( ((x>> 6)&0x3F)+0x80 )) \
            $(( (x&0x3F)+0x80 ))"
    else                        # 3+6+6+6
        y=" $(( ((x>>18)&0x07)+0xF0 )) \
            $(( ((x>>12)&0x3F)+0x80 )) \
            $(( ((x>> 6)&0x3F)+0x80 )) \
            $(( (x&0x3F)+0x80 ))"
    fi
    printf -v y '\\x%x' $y
    echo -n -e $y
}

# test
for (( i=0x2500; i<0x2600; i++ )); do
    UnicodePointToUtf8 $i
    [ "$(( i+1 & 0x1f ))" != 0 ] || echo ""
done
x='U+2620'
echo "$x -> $(UnicodePointToUtf8 $x)"

Salida:

─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟
┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿
╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟
╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿
▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟
■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿
◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟
◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿
U+2620 -> 
Дмитрий Юдин
fuente
0

Si se conoce el valor hexadecimal del carácter unicode

H="2620"
printf "%b" "\u$H"

Si se conoce el valor decimal de un carácter unicode

declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"
philcolbourn
fuente