¿Cómo imprimo un carácter ASCII por diferentes puntos de código en Bash?


12

En la tabla ASCII existe el carácter 'J' que tiene puntos de código en diferentes sistemas de numeración:

Oct   Dec   Hex   Char
112   74    4A    J

Es posible imprimir este carácter por un punto de código octal imprimiendo printf '\112'o echo $'\112'. ¿Cómo imprimo el mismo carácter mediante presentaciones de punto de código decimal y hexadecimal?


Respuestas:


12

Maleficio:

printf '\x4a'

Dic:

printf "\\$(printf %o 74)"

Alternativa para hexadecimal :-)

xxd -r <<<'0 4a'

Afortunadamente, esto también funciona awk.
Sridhar Sarnobat


6

En general, el shell podría comprender números hexadecimales, oct y decimales en variables, siempre que se hayan definido como integers:

$ declare -i v1 v2 v3 v4 v5 v6 v7
$ v1=0112
$ v2=74
$ v3=0x4a
$ v4=8#112
$ v5=10#74
$ v6=16#4a
$ v7=18#gg
echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304

O son el resultado de una "Expansión Aritmética":

$ : $(( v1=0112, v2=74, v3=0x4a, v4=8#112, v5=10#74, v6=16#4a, v7=18#gg ))
$ echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304

Entonces, solo necesita una forma de imprimir el carácter que pertenece a un valor variable.
Pero aquí hay dos formas posibles:

$ var=$((0x65))
$ printf '%b\n' "\\$(printf '0%o' "$var")"
e

$ declare -i var
$ var=0x65; printf '%b\n' "\U$(printf '%08x' "$var")"
e

Se necesitan los dos printf, uno para transformar el valor en una cadena hexadecimal y el segundo para imprimir realmente el carácter.

El segundo imprimirá cualquier punto UNICODE (si su consola está configurada correctamente).
Por ejemplo:

$ var=0x2603; printf '%b\n' "\U$(printf '%08x' "$var")"

Un hombre de nieve

El personaje que tiene una representación utf-8 como f0 9f 90 aees 0x1F42E. Buscar cow face site:fileformat.infoa conseguirlo :

$ var=0x1F42F; printf '%b\n' "\U$(printf '%08x' "$var")"
🐮

Nota : Hay un problema con la forma UNICODE en que para bash antes de 4.3 (corregido en esa versión y hacia arriba), los caracteres entre UNICODE puntos 128 y 255 (en decimal) pueden imprimirse incorrectamente.


Referencias

Cuarto párrafo adentro PARAMETERSen man bash:

Si la variable tiene su conjunto de atributos enteros, entonces el valor se evalúa como una expresión aritmética incluso si no se usa la expansión $ ((...)) (ver Expansión aritmética a continuación).

Dentro de "EVALUACIÓN ARITMÉTICA" en man bash:

Las constantes con un 0 inicial se interpretan como números octales. Un 0x o 0X inicial denota hexadecimal. De lo contrario, los números toman la forma [base #] n, donde la base opcional es un número decimal entre 2 y 64 que representa la base aritmética, yn es un número en esa base. Si se omite la base #, se utiliza la base 10. Los dígitos mayores que 9 están representados por las letras minúsculas, las letras mayúsculas, @ y _, en ese orden. Si la base es menor o igual a 36, ​​las letras minúsculas y mayúsculas se pueden usar indistintamente para representar números entre 10 y 35.


@ StéphaneChazelas Bueno, un punto de código no es (siempre) un valor de byte. Bash (en versiones anteriores a 4.3) proporciona el valor de byte del punto de código. Es decir: el carácter é(Octal: 351, Dec: 233, Hex: 0xE9) se imprime incorrectamente printf '\351'ya que imprime un valor de byte de 0xE9siempre. Para un terminal con una codificación de ISO-8859-1(y primos) que puede funcionar, pero en terminales codificados utf-8, un valor de byte 0xE9debería aparecer como . cont ....
Isaac

@ StéphaneChazelas No soy el primero en notar y buscar "bash 4.2 codifica incorrectamente" para un ejemplo. Se ha corregido desde bash 4.3 y superior.
Isaac

OKAY. Ahora entiendo lo que quieres decir (estaba probando con 4.3 según la versión anterior de tu respuesta). Tenga en cuenta que solo es bash-4.2, bash-4.1 no es compatible \u(que proviene de zsh).
Stéphane Chazelas

5

Decimal:

chr() {
    local c
    for c
    do
        printf "\\$((c/64*100+c%64/8*10+c%8))"
    done
}

chr 74

Maleficio:

chr $((16#4a))

La función puede hacer secuencias:

$ chr 74 75 76; echo
JKL
$

0

Puede usar la biblioteca POSIX Awk stdlib :

$ awklib 'BEGIN {print str_chr(74)}'
J

$ awklib 'BEGIN {print str_chr(+base_conv("4A", 16, 10))}'
J

$ awklib 'BEGIN {print str_chr(+base_conv(112, 8, 10))}'
J

$ awklib 'BEGIN {print str_chr(+base_conv(1001010, 2, 10))}'
J

0

Si tiene una lista de números para convertir y desea evitar una llamada a la función y crear una subshell para cada carácter, puede definir el conjunto ascii de antemano:

ascii=$(for x in {0..9} {A..F}; do for y in {0..9} {A..F}; do echo -ne "\x$x$y"; done; done)

Tenga en cuenta que el carácter nulo está excluido, por lo que cada carácter está compensado por 1.

Luego use algo como esto (supone 1 número por línea):

while read c; do out+="${ascii:$c-1:1}"; done <<< "$in"
echo "$out"

0

Aquí están todas las conversiones usando printf:

printf "%o" "'J" # 112 (oct)
printf "%d" "'J" # 74 (dec)
printf "%x" "'J" # 4a (hex)

printf '\112' # J (oct)
printf "\x$(printf %x 74)" # J (dec, requires double conversion)
printf '\x4a' # J (hex)
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.