Come riecheggi un carattere Unicode a 4 cifre in Bash?


224

Vorrei aggiungere il teschio e le tibie incrociate Unicode al mio prompt della shell (in particolare "SKULL AND CROSSBONES" (U + 2620)), ma non riesco a capire l'incantesimo magico per far risuonare l'eco, o qualsiasi altro, Carattere Unicode a 4 cifre. Uno a due cifre è facile. Ad esempio, echo -e "\ x55",.

Oltre alle risposte che seguono, va notato che, ovviamente, il tuo terminale deve supportare Unicode affinché l'output sia quello che ti aspetti. gnome-terminal fa un buon lavoro, ma non è necessariamente attivato di default.

Sull'app Terminale di macOS Vai su Preferenze-> Codifiche e scegli Unicode (UTF-8).


7
Nota che il tuo commento "2 digit one are easy (to echo)" è valido solo per valori fino a "\x7F"in una localizzazione UTF-8 (che il bashtag suggerisce il tuo è) ... i pattern rappresentati da un singolo byte non sono mai nell'intervallo \x80-\xFF. Questo intervallo è illegale nei caratteri UTF-8 a byte singolo. ad es. un valore Unicode Codepoint di U+0080(cioè. \x80) è in realtà 2 byte in UTF-8 .. \xC2\x80..
Peter.O

4
Es printf "\\u007C\\u001C".
Kenorb,

NB: per me in gnome-terminal, echo -e '\ufc'non produce un ü, anche con la codifica dei caratteri impostata su UTF-8. Tuttavia, ad esempio urxvt, stampa ad esempio printf "\\ub07C\\ub01C"come previsto (non con una o una scatola).
Isomorfismi

@ Peter.O Perché il bashtag è un suggerimento così utile? Sono diversi i terminali comuni in CJK o ...?
isomorfismi

1
@ Peter.O zsh, fish, scsh, elfish, ecc ... ci sono molte conchiglie diverse, ognuna può gestire caratteri unicode come vogliono (o no). "bash" chiarisce che questa domanda non riguarda una strana shell che fa le cose in modo diverso.
Masukomi,

Risposte:


237

In UTF-8 sono in realtà 6 cifre (o 3 byte).

$ printf '\xE2\x98\xA0'

Per verificare come è codificato dalla console, utilizzare hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003

5
Le mie uscite " " invece di ☠ ... Perché?
trusktr,

8
È vero. Ho scoperto che stavo usando LANG=Cinvece di LANG=en_US.UTF-8. Ora i miei terminali in Gnome mostrano correttamente i simboli ... I terminali reali (tty1-6) comunque non lo fanno.
trusktr,

6
Per quelle persone che cercano un hexdump: si 0000000 f0 9f 8d batraduce in \xf0\x9f\x8d\xba. Esempio eco: echo -e "\xf0\x9f\x8d\xba".
Blaise,

8
È inoltre possibile utilizzare la $'...'sintassi per ottenere il carattere codificato in una variabile senza utilizzare una $(...)subshell di acquisizione, da utilizzare in contesti che non interpretano loro stessi le sequenze di escape:skull=$'\xE2\x98\xA0'
Andrew Janke,

7
Un'altra cosa su hexdump: sulla mia macchina, viene emesso il secondo comando nelle risposte 0000000 98e2 00a0. Ovviamente si 0000000tratta solo di un offset irrilevante, ma i byte dopo si traducono in \xe2\x98\xa0, poiché la macchina utilizza il piccolo ordine di byte endian.
sigalor,

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

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

Funziona in Zsh (ho verificato la versione 4.3) e in Bash 4.2 o versioni successive.


16
che sputa appena lo faccio.
Masukomi,

Anche per me. Quale shell stai usando, Juliano?
Joachim Sauer

2
Scusa, ho dimenticato di dire che uso zsh.
Juliano

32
Il supporto per \ u è stato aggiunto in Bash 4.2.
Lri,

4
NON funziona per me, Mac OS 10.14.2, bash (GNU bash, versione 3.2.57 (1) -release (x86_64-apple-darwin18)). Stampa semplicemente l'input - $ echo -e '\ u2620' <invio> stampa semplicemente: \ u2620
Motti Shneor

68

Finché i tuoi editor di testo possono far fronte a Unicode (presumibilmente codificato in UTF-8) puoi inserire direttamente il punto di codice Unicode.

Ad esempio, nell'editor di testo di Vim si entra nella modalità di inserimento e si preme Ctrl+ V+ Ue quindi il numero del punto di codice come numero esadecimale di 4 cifre (pad con zeri se necessario). Quindi digitare Ctrl+ V+ U 2 6 2 0. Vedi: Qual è il modo più semplice per inserire caratteri Unicode in un documento?

In un terminale che esegue Bash, digitare CTRL+ SHIFT+ Ue digitare il punto di codice esadecimale del carattere desiderato. Durante l'immissione, il cursore dovrebbe mostrare un sottolineato u. La prima non cifra digitata termina l'input e rende il carattere. Quindi potresti essere in grado di stampare U + 2620 in Bash usando il seguente:

echo CTRL+ SHIFT+U2620ENTERENTER

(Il primo invio termina l'input Unicode e il secondo esegue il echocomando.)

Credito: Chiedi a Ubuntu SE


1
Una buona fonte per i punti di codice esadecimali
RobM

1
La versione di vim che sto usando (7.2.411 su RHEL 6.3) non risponde come desiderato quando c'è un punto tra ctrl-v e u, ma funziona bene quando quel punto viene omesso.
Chris Johnson,

@ChrisJohnson: ho rimosso il punto dalle istruzioni, non era inteso per essere un tasto premuto (motivo per cui non è apparso con l'effetto tastiera). Dispiace per la confusione.
RobM,

5
Attenzione: funziona in un terminale che esegue Bash solo se lo stai eseguendo in ambiente GTK + , come Gnome.
n.

1
La capacità di C-S-u 2 6 2 0è una funzionalità dell'emulatore di terminale, X Input Method (XIM) o simile. AFAIK, non sarai in grado di inviare entrambi SHIFTe CTRLal livello terminale. Il terminale parla solo in caratteri, piuttosto che in palestre e codici chiave come il tuo server X (inoltre, è a 7 bit a tutti gli effetti). In questo mondo, CTRLmaschera i 4 bit più significativi (& 0b00001111) che risulta in
nabin-info

31

Ecco un'implementazione Bash completamente interna, senza biforcazione, dimensioni illimitate di caratteri 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

L'output era:

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

Sono molto curioso il ragionamento alla base del metodo round-about e l'uso specifico della variabile REPLY. Immagino che tu abbia ispezionato la fonte di bash o che tu abbia attraversato o qualcosa da ottimizzare, che posso vedere come le tue scelte potrebbero essere ottimizzate, sebbene fortemente dipendenti dall'interprete).
nabin-info

14

Inserisci "☠" nello script della shell. Con le impostazioni internazionali corrette e su una console abilitata per Unicode, verrà stampato correttamente:

$ echo 

$

Una brutta "soluzione" sarebbe l'output della sequenza UTF-8, ma ciò dipende anche dalla codifica utilizzata:

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

$

13

Quick one-liner per convertire i caratteri UTF-8 nel loro formato a 3 byte:

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

5
Non chiamerei l'esempio sopra rapido (con 11 comandi e i loro parametri) ... Inoltre gestisce solo caratteri UTF-8 a 3 byte` (i caratteri UTF-8 possono essere 1, 2 o 3 byte) ... Questo è un po 'più corto e funziona per 1-3 ++++ byte: printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... xxd viene spedito come parte del pacchetto' vim-common '
Peter.O

PS: Ho appena notato che l'esempio hexdump / awk sopra sta spostando la sequenza di byte in una coppia di byte. Questo non si applica a un dump UTF-8. Sarebbe relaventoso se fosse un dump di UTF-16LE e volesse produrre Unicode Codepoints , ma non ha senso qui dato che l'input è UTF-8 e l'output è esattamente come input (più il \ x prima di ogni hexdigit -pair)
Peter.O

7
I caratteri UTF-8 possono avere una sequenza di 1 - 4 byte
cms

1
sulla base del commento di @ Peter.O, trovo quanto segue, sebbene più grande, abbastanza utile: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
Buon Dio. Considera: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ... buon divertimento 👍
nabin-info

8

Sto usando questo:

$ echo -e '\u2620'

È molto più semplice che cercare una rappresentazione esadecimale ... Sto usando questo nei miei script di shell. Funziona su gnome-term e urxvt AFAIK.


2
@masukomi se sai come usare brew puoi installare una bash più recente e usarla. Quanto sopra funziona bene sul mio terminale mac quando si utilizza la bash aggiornata.
mcheema,

Sì, va bene con le versioni più recenti di bash. Stringhe prompt di Hower, ad esempio $ PS1, non utilizzano i formati di escape dell'eco
cms,

6

Potrebbe essere necessario codificare il punto di codice come ottale affinché la rapida espansione lo decodifichi correttamente.

U + 2620 codificato come UTF-8 è E2 98 A0.

Quindi a Bash,

export PS1="\342\230\240"

renderà il tuo guscio pronto in teschio e ossa.


ciao, qual è il codice che dovrei inserire per "e0 b6 85"? come posso trovarlo?
Udayantha Udy Warnasuriya,

basta convertire i numeri esadecimali (base 16) e0 b6 85 in ottali (base 8) - utilizzare una calcolatrice è probabilmente il modo più semplice per farlo
cms

e0 b6 85 hex è 340 266 205 ottale
cm

Questo ha funzionato, grazie mille! E a proposito, è possibile findal ottale versione in queste pagine: graphemica.com/%E2%9B%B5
Perlnika

6

In bash per stampare un carattere Unicode per l'output usa \ x, \ u o \ U (prima per esadecimale a 2 cifre, seconda per esadecimale a 4 cifre, terza per qualsiasi lunghezza)

echo -e '\U1f602'

Voglio assegnarlo a una variabile usando la sintassi $ '...'

x=$'\U1f602'
echo $x

5

Se non ti dispiace un one-liner Perl:

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

-CSabilita la decodifica UTF-8 sull'ingresso e la codifica UTF-8 sull'uscita. -Evaluta il prossimo argomento come Perl, con funzionalità moderne come sayabilitate. Se non vuoi una nuova riga alla fine, usa printinvece di say.


5

Uno di questi tre comandi stamperà il carattere desiderato in una console, a condizione che la console accetti i caratteri UTF-8 (la maggior parte di quelli attuali lo fanno):

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) 

Successivamente, è possibile copiare e incollare il glifo (immagine, carattere) effettivo in qualsiasi editor di testo (abilitato UTF-8).

Se hai bisogno di vedere come tale Unicode Code Point è codificato in UTF-8, usa xxd (visualizzatore esadecimale molto meglio di 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

Oppure, in esadecimale per evitare errori: 0xE2 0x98 0xA0. Cioè, i valori tra lo spazio (HEX 20) e il Line-Feed (Hex 0A).

Se si desidera un tuffo in profondità nella conversione dei numeri a caratteri: un'occhiata qui per vedere un articolo del wiki di Greg (BashFAQ) sulla codifica ASCII in Bash!


ri: "O, in HEX per evitare errori ..." Difficilmente penso che convertire un carattere unicode in una codifica binaria che esprimi in caratteri esadecimali, aiuti a evitare errori. L'uso della notazione unicode in "bash" eviterebbe meglio gli errori, ad esempio: "\ uHHHH --- il carattere Unicode (ISO / IEC 10646) il cui valore è ---- valore esadecimale HHHH (da una a quattro cifre esadecimali); \ UHHHHHHHH ---- il carattere Unicode (ISO / IEC 10646) il cui valore è il ---- valore esadecimale HHHHHHHH (da una a otto cifre esadecimali)
Astara

4

Il printfbuiltin (proprio come i coreutils ' printf) conosce la \usequenza di escape che accetta caratteri Unicode a 4 cifre:

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

Test con Bash 4.2.37 (1):

$ printf '\u2620\n'

printf è anche una shell integrata. Probabilmente stai usando il macOS bash predefinito (v3). Prova \printfa usare l'eseguibile standalone, oppure prova con bash aggiornato
mcint

4

Ci scusiamo per aver rianimato questa vecchia domanda. Ma quando si usa bashc'è un approccio molto semplice per creare punti di codice Unicode da un semplice input ASCII, che addirittura non si biforca affatto:

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

Usalo come segue per definire alcuni punti di codice

unicode crossbones 0x2620
echo "$crossbones"

o per scaricare i primi 65536 codici unicode su stdout (impiega meno di 2 secondi sulla mia macchina. Lo spazio aggiuntivo è impedire a determinati caratteri di fluire l'uno nell'altro a causa del carattere monospace della shell):

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

o per raccontare un po 'la storia di un genitore molto tipica (questo richiede Unicode 2010):

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

Spiegazione:

  • printf '\UXXXXXXXX' stampa qualsiasi carattere Unicode
  • printf '\\U%08x' numberstampa \UXXXXXXXXcon il numero convertito in esadecimale, questo viene quindi inviato ad un altro printfper stampare effettivamente il carattere Unicode
  • printf riconosce ottale (0oct), esadecimale (0xHEX) e decimale (0 o numeri che iniziano da 1 a 9) come numeri, in modo da poter scegliere la rappresentazione più adatta
  • printf -v var ..raccoglie l'output di printfin una variabile, senza fork (che accelera enormemente le cose)
  • local variable è lì per non inquinare lo spazio dei nomi globale
  • local -n var=otheralias vara other, tale che l'incarico di varaltera other. Una parte interessante qui è che varfa parte dello spazio dei nomi locale, mentre otherfa parte dello spazio dei nomi globale.
    • Si noti che non esiste qualcosa come localo globalspazio dei nomi in bash. Le variabili sono mantenute nell'ambiente e sono sempre globali. Local rimuove semplicemente il valore corrente e lo ripristina quando la funzione viene lasciata di nuovo. Altre funzioni richiamate dall'interno della funzione localvedranno comunque il valore "locale". Questo è un concetto fondamentalmente diverso rispetto a tutte le normali regole di scoping trovate in altre lingue (e ciò che bashfa è molto potente ma può portare a errori se sei un programmatore che non ne è consapevole).

bene - non funziona affatto per me. qualsiasi tentativo di usare una qualsiasi delle tue funzioni, emette: riga 6: local: -n: opzione non valida local: use: nome locale [= valore] ... Sto usando gli ultimi (10.14.2) MacOS e bash (GNU bash , versione 3.2.57 (1) -release (x86_64-apple-darwin18))
Motti Shneor

4

Ecco un elenco di tutte le emoji unicode disponibili:

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

Esempio:

echo -e "\U1F304"
🌄

Per ottenere il valore ASCII di questo personaggio usa hexdump

echo -e "🌄" | hexdump -C

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

E quindi usa i valori informati in formato esadecimale

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

l'eco della stringa \ U <hex> non funziona su OSX ma genera esattamente quello che c'è tra virgolette.
Masukomi,


2

Facile con una fodera Python2 / 3:

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

Risultati in:


2

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

Produzione:

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

0

Se è noto il valore esadecimale del carattere Unicode

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

Se è noto il valore decimale di un carattere unicode

declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.