Comment imprimer un caractère ASCII par différents points de code dans Bash?


12

Dans le tableau ASCII, le caractère «J» existe qui a des points de code dans différents systèmes numériques:

Oct   Dec   Hex   Char
112   74    4A    J

Il est possible d'imprimer ce caractère par un point de code octal en imprimant printf '\112'ou echo $'\112'. Comment imprimer le même caractère par des présentations de point de code décimal et hexadécimal?


Réponses:


12

Hex:

printf '\x4a'

Déc:

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

Alternative pour hex :-)

xxd -r <<<'0 4a'

Heureusement, cela fonctionne également awk.
Sridhar Sarnobat


6

En général, le shell peut comprendre les nombres hexadécimaux, octaux et décimaux dans les variables, à condition qu'ils soient définis comme 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

Ou ils sont le résultat d'une "expansion arithmétique":

$ : $(( 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

Ainsi, vous avez juste besoin d'une façon d'imprimer le caractère qui appartient à une valeur de variable.
Mais voici deux manières possibles:

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

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

Les deux printf sont nécessaires, l'un pour transformer la valeur en une chaîne hexadécimale et le second pour réellement imprimer le caractère.

Le second imprimera n'importe quel point UNICODE (si votre console est correctement définie).
Par exemple:

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

Un bonhomme de neige.

Le caractère qui a une représentation UTF-8 f0 9f 90 aeest 0x1F42E. Rechercher cow face site:fileformat.infopour obtenir :

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

Remarque : Il y a un problème avec la manière UNICODE en ce que pour bash avant 4.3 (corrigé dans cette version et vers le haut), les caractères entre les points UNICODE 128 et 255 (en décimal) peuvent être imprimés incorrectement.


Références

Quatrième paragraphe à l'intérieur PARAMETERSde man bash:

Si la variable a son attribut entier défini, la valeur est évaluée comme une expression arithmétique même si l'expansion $ ((...)) n'est pas utilisée (voir Expansion arithmétique ci-dessous).

Dans "ÉVALUATION ARITHMÉTIQUE" en man bash:

Les constantes avec un 0 en tête sont interprétées comme des nombres octaux. Un 0x ou 0X en tête indique hexadécimal. Sinon, les nombres prennent la forme [base #] n, où la base facultative est un nombre décimal compris entre 2 et 64 représentant la base arithmétique, et n est un nombre dans cette base. Si la base # est omise, la base 10 est utilisée. Les chiffres supérieurs à 9 sont représentés par les lettres minuscules, les lettres majuscules, @ et _, dans cet ordre. Si la base est inférieure ou égale à 36, les lettres minuscules et majuscules peuvent être utilisées de manière interchangeable pour représenter des nombres compris entre 10 et 35.


@ StéphaneChazelas Eh bien, un point de code n'est pas (toujours) une valeur d'octet. Bash (dans les versions antérieures à 4.3) fournit la valeur d'octet du point de code. C'est-à-dire: le caractère é(Octal: 351, Dec: 233, Hex: 0xE9) est incorrectement imprimé avec printf '\351'car il imprime une valeur d'octet de 0xE9toujours. Pour un terminal avec un encodage de ISO-8859-1(et des cousins) qui peut fonctionner, mais dans les terminaux encodés en utf-8, une valeur d'octet de 0xE9devrait apparaître comme . suite ....
Isaac

@ StéphaneChazelas Je ne suis pas le premier à remarquer et à rechercher "bash 4.2 mal encodé" pour un exemple. Il a été corrigé depuis bash 4.3 et supérieur.
Isaac

D'ACCORD. Je vois ce que vous voulez dire maintenant (je testais avec 4.3 selon la version précédente de votre réponse). Notez que ce n'est que bash-4.2, bash-4.1 ne prend pas en charge \u(qui vient de zsh).
Stéphane Chazelas

5

Décimal:

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

chr 74

Hex:

chr $((16#4a))

La fonction peut faire des séquences:

$ chr 74 75 76; echo
JKL
$

0

Vous pouvez utiliser la bibliothèque stdlib POSIX Awk :

$ 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 vous avez une liste de nombres à convertir et que vous souhaitez éviter un appel de fonction et la création d'un sous-shell pour chaque caractère, vous pouvez définir au préalable l'ensemble ascii:

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

Notez que le caractère nul est exclu, donc chaque caractère est compensé par 1.

Ensuite, utilisez quelque chose comme ça (en supposant 1 numéro par ligne):

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

0

Voici toutes les conversions utilisant 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)
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.