Comment faire écho à un caractère Unicode à 4 chiffres dans Bash?


224

Je voudrais ajouter le crâne et les os croisés Unicode à mon invite de shell (en particulier le `` SKULL AND CROSSBONES '' (U + 2620)), mais je ne peux pas comprendre l'incantation magique pour faire résonner l'écho, ou tout autre, Caractère Unicode à 4 chiffres. Les deux chiffres sont faciles. Par exemple, echo -e "\ x55",.

En plus des réponses ci-dessous, il convient de noter que, évidemment, votre terminal doit prendre en charge Unicode pour que la sortie soit ce que vous attendez. gnome-terminal fait un bon travail, mais il n'est pas nécessairement activé par défaut.

Sur l'application Terminal de macOS Allez dans Préférences-> Encodages et choisissez Unicode (UTF-8).


7
Notez que votre commentaire "les deux chiffres sont faciles (pour faire écho)" n'est valide que pour les valeurs allant jusqu'à "\x7F"un environnement UTF-8 (ce que la bashbalise suggère que le vôtre est) ... les modèles représentés par un seul octet ne sont jamais dans la plage \x80-\xFF. Cette plage est illégale dans les caractères UTF-8 à un octet. par exemple, une valeur Unicode Codepoint de U+0080(ie. \x80) est en fait de 2 octets en UTF-8 .. \xC2\x80..
Peter.O

4
Par exemple printf "\\u007C\\u001C".
kenorb

NB: pour moi in gnome-terminal, echo -e '\ufc'ne produit pas de ü, même avec un encodage de caractères réglé sur UTF-8. Cependant, par exemple, urxvtimprime par exemple printf "\\ub07C\\ub01C"comme prévu (pas avec un ou une boîte).
isomorphismes

@ Peter.O Pourquoi la bashbalise est-elle un indice si utile? Les différents terminaux sont-ils communs dans CJK ou…?
isomorphismes

1
@ Peter.O zsh, fish, scsh, elfish, etc ... il existe de nombreux coquillages différents, chacun peut gérer les caractères unicode comme bon lui semble (ou pas). "bash" indique clairement que cette question ne concerne pas un shell bizarre qui fait les choses différemment.
masukomi

Réponses:


237

En UTF-8, il s'agit en fait de 6 chiffres (ou 3 octets).

$ printf '\xE2\x98\xA0'

Pour vérifier comment il est encodé par la console, utilisez hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003

5
La mine produit " " au lieu de ☠ ... Pourquoi?
trusktr

8
C'est vrai. J'ai découvert que j'utilisais LANG=Cau lieu de LANG=en_US.UTF-8. Maintenant, mes terminaux dans Gnome affichent correctement les symboles ... Les vrais terminaux (tty1-6) ne le font toujours pas.
trusktr

6
Pour ceux qui essaient un hexdump: se 0000000 f0 9f 8d batraduit par \xf0\x9f\x8d\xba. Exemple écho: echo -e "\xf0\x9f\x8d\xba".
Blaise

8
Vous pouvez également utiliser la $'...'syntaxe pour obtenir le caractère encodé dans une variable sans utiliser de $(...)sous-shell de capture, pour une utilisation dans des contextes qui n'interprètent pas eux-mêmes les séquences d'échappement:skull=$'\xE2\x98\xA0'
Andrew Janke

7
Une autre chose à propos de hexdump: sur ma machine, la deuxième commande dans les sorties de réponse 0000000 98e2 00a0. Bien sûr, 0000000c'est juste un décalage sans importance, mais les octets après cela se traduisent par \xe2\x98\xa0, car la machine utilise le petit ordre d'octets endian.
sigalor

98
% echo -e '\u2620'     # \u takes four hexadecimal digits

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

Cela fonctionne dans Zsh (j'ai vérifié la version 4.3) et dans Bash 4.2 ou plus récent.


16
qui crache juste \ u2620 quand je le fais.
masukomi

Pour moi aussi. Quelle coquille utilisez-vous, Juliano?
Joachim Sauer

2
Désolé, j'ai oublié de dire que j'utilise zsh.
Juliano

32
La prise en charge de \ u a été ajoutée dans Bash 4.2.
Lri

4
ne fonctionne PAS pour moi, Mac OS 10.14.2, bash (GNU bash, version 3.2.57 (1) -release (x86_64-apple-darwin18)). Il affiche simplement l'entrée - $ echo -e '\ u2620' <enter> affiche simplement: \ u2620
Motti Shneor

68

Tant que vos éditeurs de texte peuvent faire face à Unicode (vraisemblablement encodé en UTF-8), vous pouvez entrer directement le point de code Unicode.

Par exemple, dans l' éditeur de texte Vim, vous entrez en mode d'insertion et appuyez sur Ctrl+ V+ Upuis sur le numéro de point de code sous la forme d'un nombre hexadécimal à 4 chiffres (complétez avec des zéros si nécessaire). Vous devez donc taper Ctrl+ V+ U 2 6 2 0. Voir: Quelle est la façon la plus simple d'insérer des caractères Unicode dans un document?

Sur un terminal exécutant Bash, vous devez taper CTRL+ SHIFT+ Uet taper le point de code hexadécimal du caractère souhaité. Pendant la saisie, votre curseur doit afficher un souligné u. Le premier non numérique que vous saisissez termine la saisie et affiche le caractère. Ainsi, vous pourrez imprimer U + 2620 dans Bash en utilisant ce qui suit:

echo CTRL+ SHIFT+U2620ENTERENTER

(La première entrée termine l'entrée Unicode et la seconde exécute la echocommande.)

Crédit: Ask Ubuntu SE


1
Une bonne source pour les points de code hexadécimaux est unicodelookup.com/#0x2620/1
RobM

1
La version de vim que j'utilise (7.2.411 sur RHEL 6.3) ne répond pas comme souhaité quand il y a un point entre le ctrl-v et le u, mais fonctionne bien lorsque ce point est omis.
Chris Johnson

@ChrisJohnson: J'ai supprimé le point des instructions, il n'était pas destiné à être une touche (c'est pourquoi il n'apparaissait pas avec l'effet clavier). Désolé pour la confusion.
RobM

5
Attention: cela ne fonctionne dans un terminal exécutant Bash que si vous l'exécutez sous environnement GTK + , comme Gnome.
nr

1
La capacité de C-S-u 2 6 2 0est une fonctionnalité de votre émulateur de terminal, méthode d'entrée X (XIM) ou similaire. AFAIK, vous ne pourrez pas envoyer les deux SHIFTet CTRLvers la couche terminale. Le terminal ne parle qu'en caractères, plutôt qu'en clés et codes clés comme votre serveur X (également, il est 7 bits à toutes fins utiles). Dans ce monde, CTRLmasque les 4 bits les plus significatifs (& 0b00001111), ce qui donne
nabin-info

31

Voici une implémentation Bash entièrement interne, pas de fourche, taille illimitée de caractères 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 sortie était:

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

Je suis très curieux du raisonnement derrière la méthode du rond-point et de l'utilisation spécifique de la variable REPLY. Je suppose que vous avez inspecté la source bash ou parcouru ou quelque chose à optimiser, que je peux voir comment vos choix pourraient être optimisés, bien que très dépendants de l'interpréteur).
nabin-info

14

Mettez simplement "☠" dans votre script shell. Dans les paramètres régionaux corrects et sur une console compatible Unicode, cela s'imprimera très bien:

$ echo 

$

Une "solution de contournement" laide serait de sortir la séquence UTF-8, mais cela dépend aussi du codage utilisé:

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

$

13

Une ligne rapide pour convertir les caractères UTF-8 dans leur format à 3 octets:

var="$(echo -n '☠' | od -An -tx1)"; printf '\\x%s' ${var^^}; echo

5
Je n'appellerais pas l'exemple ci-dessus rapidement (avec 11 commandes et leurs paramètres) ... De plus, il ne gère que les caractères UTF-8 de 3 octets (les caractères UTF-8 peuvent être de 1, 2 ou 3 octets) ... est un peu plus court et fonctionne pour 1-3 ++++ octets: printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... xxd est expédié dans le cadre du package 'vim-common'
Peter.O

PS: Je viens de remarquer que l'exemple hexdump / awk ci-dessus modifie la séquence d'octets dans une paire d'octets. Cela ne s'applique pas à un vidage UTF-8. Ce serait pertinent si c'était un vidage de UTF-16LE et que vous vouliez sortir des points de code Unicode , mais cela n'a pas de sens ici car l'entrée est UTF-8 et la sortie est exactement comme entrée (plus le \ x avant chaque chiffre hexadécimal) -pair)
Peter.O

7
Les caractères UTF-8 peuvent être des séquences de 1 à 4 octets
cms

1
basé sur le commentaire de @ Peter.O, je trouve ce qui suit, bien que plus grand, assez pratique: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
Bon Dieu. Considérez: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ... profitez 👍
nabin-info

8

J'utilise ceci:

$ echo -e '\u2620'

C'est plus facile que de chercher une représentation hexadécimale ... J'utilise ceci dans mes scripts shell. Cela fonctionne sur gnome-term et urxvt AFAIK.


2
@masukomi si vous savez utiliser brew, vous pouvez installer un bash plus récent et l'utiliser. Ce qui précède fonctionne bien sur mon terminal mac lors de l'utilisation de bash mis à niveau.
mcheema

Oui, ça va avec les nouvelles versions de bash. Les chaînes d'invite Hower, par exemple $ PS1 n'utilisent pas les formats d'échappement d'écho
cms

6

Vous devrez peut-être coder le point de code en octal pour que l'expansion rapide le décode correctement.

U + 2620 codé en UTF-8 est E2 98 A0.

Donc, dans Bash,

export PS1="\342\230\240"

rendra votre coquille rapide en crâne et en os.


salut, quel est le code que je dois entrer pour "e0 b6 85"? comment le trouver?
Udayantha Udy Warnasuriya

il suffit de convertir les nombres hexadécimaux (base 16) e0 b6 85 en octal (base 8) - utiliser une calculatrice est probablement le moyen le plus simple de le faire
cms

e0 b6 85 hex est 340266205 octal
cms

Cela a fonctionné, merci beaucoup! Et d' ailleurs, vous pouvez findal la version octal à ces pages: graphemica.com/%E2%9B%B5
Perlnika

6

En bash pour imprimer un caractère Unicode pour la sortie, utilisez \ x, \ u ou \ U (premier pour un hexadécimal à 2 chiffres, second pour un hexadécimal à 4 chiffres, troisième pour n'importe quelle longueur)

echo -e '\U1f602'

Si vous voulez l'affecter à une variable, utilisez la syntaxe $ '...'

x=$'\U1f602'
echo $x

5

Si cela ne vous dérange pas un Perl one-liner:

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

-CSactive le décodage UTF-8 en entrée et le codage UTF-8 en sortie. -Eévalue l'argument suivant comme Perl, avec des fonctionnalités modernes comme sayactivé. Si vous ne voulez pas de nouvelle ligne à la fin, utilisez printplutôt que say.


5

Chacune de ces trois commandes imprimera le caractère souhaité dans une console, à condition que la console accepte les caractères UTF-8 (les plus courants le font):

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) 

Après, vous pouvez copier et coller le glyphe réel (image, caractère) dans n'importe quel éditeur de texte (activé UTF-8).

Si vous avez besoin de voir comment un tel point de code Unicode est encodé en UTF-8, utilisez xxd (une visionneuse hexagonale bien meilleure 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

Ou, dans HEX pour éviter les erreurs: 0xE2 0x98 0xA0. Autrement dit, les valeurs entre l'espace (HEX 20) et le saut de ligne (Hex 0A).

Si vous voulez approfondir la conversion des nombres en caractères: regardez ici pour voir un article du wiki de Greg (BashFAQ) sur le codage ASCII dans Bash!


re: "Ou, dans HEX pour éviter les erreurs ..." Je pense à peine que la conversion d'un caractère unicode en un codage binaire que vous exprimez en caractères hexadécimaux, aide à éviter les erreurs. L'utilisation de la notation unicode dans "bash" éviterait mieux les erreurs, à savoir: "\ uHHHH --- le caractère Unicode (ISO / IEC 10646) dont la valeur est la ---- valeur hexadécimale HHHH (un à quatre chiffres hexadécimaux); \ UHHHHHHHHH ---- le caractère Unicode (ISO / IEC 10646) dont la valeur est la ---- valeur hexadécimale HHHHHHHH (un à huit chiffres hexadécimaux)
Astara

4

La fonction printfintégrée (tout comme les coreutils printf) connaît la \uséquence d'échappement qui accepte les caractères Unicode à 4 chiffres:

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

Test avec Bash 4.2.37 (1):

$ printf '\u2620\n'

printf est également un shell intégré. Vous utilisez probablement le bash macOS par défaut (v3). Essayez avec \printfpour utiliser l'exécutable autonome, ou essayez avec bash mis à niveau
mcint

4

Désolé d'avoir relancé cette vieille question. Mais lors de l'utilisation, bashil existe une approche très simple pour créer des points de code Unicode à partir d'une entrée ASCII ordinaire, qui ne débouche même pas du tout:

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; };

Utilisez-le comme suit pour définir certains points de code

unicode crossbones 0x2620
echo "$crossbones"

ou pour vider les premiers 65536 points de code unicode vers stdout (cela prend moins de 2 secondes sur ma machine. L'espace supplémentaire est d'empêcher certains caractères de s'écouler les uns dans les autres en raison de la police monospace du shell):

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

ou pour raconter l'histoire d'un parent très typique (cela nécessite Unicode 2010):

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

Explication:

  • printf '\UXXXXXXXX' imprime tout caractère Unicode
  • printf '\\U%08x' numberimprime \UXXXXXXXXavec le nombre converti en hexadécimal, celui-ci est ensuite envoyé à un autre printfpour réellement imprimer le caractère Unicode
  • printf reconnaît octal (0oct), hex (0xHEX) et décimal (0 ou nombres commençant par 1 à 9) comme des nombres, vous pouvez donc choisir la représentation qui convient le mieux
  • printf -v var ..rassemble la sortie de printfdans une variable, sans fork (ce qui accélère énormément les choses)
  • local variable est là pour ne pas polluer l'espace de noms global
  • local -n var=otheralias varà other, tels que l'affectation à varaltère other. Une partie intéressante ici est, qui varfait partie de l'espace de noms local, tandis que otherfait partie de l'espace de noms global.
    • Veuillez noter qu'il n'existe pas d' espace localou d' globalespace de noms dans bash. Les variables sont conservées dans l'environnement, et celles-ci sont toujours globales. Local supprime simplement la valeur actuelle et la restaure lorsque la fonction est à nouveau abandonnée. Les autres fonctions appelées à l'intérieur de la fonction avec localverront toujours la valeur "locale". Il s'agit d'un concept fondamentalement différent de toutes les règles de portée normales trouvées dans d'autres langages (et ce bashqui est très puissant mais peut entraîner des erreurs si vous êtes un programmeur qui n'en est pas conscient).

bien - ne fonctionne pas du tout pour moi. toute tentative d'utilisation de l'une de vos fonctions, émet: ligne 6: local: -n: option non valide local: usage: nom local [= valeur] ... J'utilise MacOS et bash (10.14.2) les plus récents (bash GNU , version 3.2.57 (1) -release (x86_64-apple-darwin18))
Motti Shneor

4

Voici une liste de tous les emoji Unicode disponibles:

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

Exemple:

echo -e "\U1F304"
🌄

Pour obtenir la valeur ASCII de ce caractère, utilisez hexdump

echo -e "🌄" | hexdump -C

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

Et puis utilisez les valeurs renseignées au format hexadécimal

echo -e "\xF0\x9F\x8C\x84\x0A"
🌄

l'écho de la chaîne \ U <hex> ne fonctionne pas sur OSX, il ne fait que reproduire exactement ce qui est entre guillemets.
masukomi


2

Facile avec une doublure Python2 / 3:

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

Résulte en:


2

Dans 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)"

Production:

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

0

Si la valeur hexadécimale du caractère unicode est connue

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

Si la valeur décimale d'un caractère unicode est connue

declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"
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.