Como você faz eco de um caractere Unicode de 4 dígitos no Bash?


224

Gostaria de adicionar o crânio e ossos cruzados Unicode ao prompt do meu shell (especificamente os 'CRÂNIO E CROSSBONES' (U + 2620)), mas não consigo descobrir o encantamento mágico para fazer o eco cuspir nele ou em qualquer outro, Caractere Unicode de 4 dígitos. Um de dois dígitos é fácil. Por exemplo, echo -e "\ x55",.

Além das respostas abaixo, observe que, obviamente, seu terminal precisa suportar Unicode para que a saída seja o que você espera. O gnome-terminal faz um bom trabalho nisso, mas não é necessariamente ativado por padrão.

No aplicativo Terminal do macOS, vá para Preferências-> Codificações e escolha Unicode (UTF-8).


7
Observe que o comentário do seu "2 dígitos é fácil (eco)" é válido apenas para valores de até "\x7F"um código de idioma UTF-8 (que a bashtag sugere que o seu seja) ... os padrões representados por um único byte nunca estão no intervalo \x80-\xFF. Esse intervalo é ilegal em caracteres UTF-8 de um byte. por exemplo, um valor de ponto de código Unicode de U+0080(ie. \x80) é na verdade 2 bytes em UTF-8 .. \xC2\x80..
Peter.O

4
Por exemplo printf "\\u007C\\u001C".
kenorb

NB: para mim gnome-terminal, echo -e '\ufc'não produz um ü, mesmo com a codificação de caracteres definida como UTF-8. No entanto, por exemplo urxvt, imprime , por exemplo, printf "\\ub07C\\ub01C"conforme o esperado (não com uma caixa ou).
Isomorphismes

@ Peter.O Por que a bashtag é uma dica tão útil? Terminais diferentes são comuns em CJK ou…?
Isomorphismes

1
@ Peter.O zsh, fish, scsh, élfico, etc ... existem muitas conchas diferentes, cada uma pode lidar com caracteres unicode da maneira que quiser (ou não). "bash" deixa claro que essa pergunta não é sobre uma casca estranha que faz as coisas de maneira diferente.
Masukomi

Respostas:


237

No UTF-8, são na verdade 6 dígitos (ou 3 bytes).

$ printf '\xE2\x98\xA0'

Para verificar como é codificado pelo console, use hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003

5
Mina produz " " em vez de Why ... Por que isso?
trusktr

8
Isso é verdade. Eu descobri que estava usando em LANG=Cvez de LANG=en_US.UTF-8. Agora, meus terminais no Gnome mostram os símbolos corretamente ... Os terminais reais (tty1-6) ainda não mostram.
trusktr

6
Para aquelas pessoas que tentam um hexdump: 0000000 f0 9f 8d batraduz para \xf0\x9f\x8d\xba. Exemplo eco: echo -e "\xf0\x9f\x8d\xba".
Blaise

8
Você também pode usar a $'...'sintaxe para obter o caractere codificado em uma variável sem usar um $(...)subshell captura, para uso em contextos que não eles mesmos interpretar as sequências de escape:skull=$'\xE2\x98\xA0'
Andrew Janke

7
Outra coisa sobre o hexdump: na minha máquina, o segundo comando na resposta é gerado 0000000 98e2 00a0. É claro que isso 0000000é apenas um deslocamento sem importância, mas os bytes após a conversão \xe2\x98\xa0, porque a máquina usa a pequena ordem de bytes endian.
Sigalor 15/05

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

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

Isso funciona no Zsh (verifiquei a versão 4.3) e no Bash 4.2 ou mais recente.


16
que cuspa \ u2620 quando eu faço.
Masukomi 02/03/09

Para mim também. Qual shell você está usando, Juliano?
Joachim Sauer

2
Desculpe, esqueci de dizer que eu uso o zsh.
Juliano

32
O suporte para \ u foi adicionado no Bash 4.2.
Lri

4
NÃO funciona para mim, Mac OS 10.14.2, bash (lançamento do GNU bash, versão 3.2.57 (1) (x86_64-apple-darwin18)). Ele apenas imprime a entrada - $ echo -e '\ u2620' <enter> simplesmente imprime: \ u2620
Motti Shneor 26/03/19

68

Desde que seus editores de texto possam lidar com Unicode (presumivelmente codificado em UTF-8), você poderá inserir o ponto de código Unicode diretamente.

Por exemplo, no editor de texto do Vim , você entra no modo de inserção e pressiona Ctrl+ V+ Ue, em seguida, o número do ponto de código como um número hexadecimal de 4 dígitos (teclado com zeros, se necessário). Então você digitaria Ctrl+ V+ U 2 6 2 0. Vejo: Qual é a maneira mais fácil de inserir caracteres Unicode em um documento?

Em um terminal executando o Bash, digite CTRL+ SHIFT+ Ue digite o ponto de código hexadecimal do caractere que você deseja. Durante a entrada, seu cursor deve mostrar um sublinhado u. O primeiro dígito que não digita termina a entrada e renderiza o caractere. Assim, você poderá imprimir o U + 2620 no Bash usando o seguinte:

echo CTRL+ SHIFT+U2620ENTERENTER

(A primeira entrada finaliza a entrada Unicode e a segunda executa o echocomando.)

Crédito: Ask Ubuntu SE


1
Uma boa fonte para os pontos de código hexademical
RobM

1
A versão do vim que estou usando (7.2.411 no RHEL 6.3) não responde conforme o desejado quando há um ponto entre ctrl-v e u, mas funciona bem quando esse ponto é omitido.
22413 Chris Johnson

@ ChrisJohnson: removi o período das instruções, não se destinava a pressionar a tecla (é por isso que não apareceu com o efeito do teclado). Desculpe pela confusão.
27413 RobM

5
Cuidado: isso funciona em um terminal executando o Bash apenas se você estiver executando no ambiente GTK + , como o Gnome.
nr

1
A capacidade de C-S-u 2 6 2 0é um recurso do emulador de terminal, XIM (X Input Method) ou similar. AFAIK, você não poderá enviar ambos SHIFTe CTRLpara a camada terminal. O terminal fala apenas em caracteres, em vez de em academias e códigos de chave como o seu servidor X (também é de 7 bits para todos os efeitos). Neste mundo, CTRLmascara os 4 bits mais significativos (& 0b00001111) que resultam em
nabin-info 4/17

31

Aqui está uma implementação Bash totalmente interna, sem bifurcação, tamanho 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

A saída foi:

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

Estou muito curioso sobre o raciocínio por trás do método round-around e o uso específico da variável REPLY. Suponho que você inspecionou a fonte do bash ou executou ou algo para otimizar, o que posso ver como suas escolhas podem ser otimizadas, embora altamente dependentes do intérprete).
Nabin-info

14

Basta colocar "☠" no seu script de shell. No local correto e em um console habilitado para Unicode, ele será impresso bem:

$ echo 

$

Uma "solução alternativa" feia seria produzir a sequência UTF-8, mas isso também depende da codificação usada:

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

$

13

Alinhamento rápido para converter caracteres UTF-8 no formato de 3 bytes:

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

5
Eu não chamaria o exemplo acima de rápido (com 11 comandos e seus parâmetros) ... Além disso, ele suporta apenas 3 bytes de caracteres UTF-8` (os caracteres UTF-8 podem ser de 1, 2 ou 3 bytes) ... é um pouco mais curto e funciona para 1-3 ++++ bytes: printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... o xxd é enviado como parte do pacote 'vim-common'
Peter.O

PS: Acabei de notar que o exemplo acima hexdump / awk está alterando a sequência de bytes em um par de bytes. Isso não se aplica a um despejo UTF-8. Seria relevante se fosse um despejo de UTF-16LE e desejasse gerar pontos de código Unicode , mas não faz sentido aqui, pois a entrada é UTF-8 e a saída é exatamente como a entrada (mais o \ x antes de cada hexdígito -pair)
Peter.O

7
Caracteres UTF-8 pode ser 1-4 bytes sequências
cms

1
baseado no comentário do @ Peter.O, acho o seguinte, enquanto maior, 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
Bom Deus, cara. Considere: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ... enjoy 👍
nabin-info

8

Eu estou usando isso:

$ echo -e '\u2620'

Isso é muito mais fácil do que pesquisar uma representação hexadecimal ... Estou usando isso nos meus scripts de shell. Isso funciona no AFAIK gnome-term e urxvt.


2
@masukomi, se você souber usar o brew, poderá instalar um bash mais recente e usá-lo. O acima funciona bem no meu terminal mac ao usar o bash atualizado.
Mcheema

Sim, tudo bem com as versões mais recentes do bash. Hower cordas rápidas, por exemplo $ PS1 não use eco formatos de fuga
CMS

6

Pode ser necessário codificar o ponto de código como octal para que a expansão rápida decodifique corretamente.

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

Então, no Bash,

export PS1="\342\230\240"

fará com que sua concha avise no crânio e nos ossos.


oi, qual é o código que devo inserir para "e0 b6 85"? como posso encontrar?
Udayantha Udy Warnasuriya

apenas converter o hexadecimal (base 16) números E0 B6 85 em octal (base 8) - usar uma calculadora é provavelmente a maneira mais fácil de fazer isso
CMS

e0 b6 85 hex é 340 266 205 octal
cms

Isso funcionou, muito obrigado! E, a propósito, você pode encontrar a versão octal local nestas páginas: graphemica.com/%E2%9B%B5
Perlnika

6

No bash para imprimir um caractere Unicode para saída, use \ x, \ u ou \ U (primeiro para hexadecimal de 2 dígitos, segundo para hexadecimal de 4 dígitos, terceiro para qualquer comprimento)

echo -e '\U1f602'

Se você deseja atribuí-lo a uma variável, use a sintaxe $ '...'

x=$'\U1f602'
echo $x

5

Se você não se importa com uma linha única do Perl:

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

-CSpermite decodificação UTF-8 na entrada e codificação UTF-8 na saída. -Eavalia o próximo argumento como Perl, com recursos modernos como sayativado. Se você não quiser uma nova linha no final, use em printvez de say.


5

Qualquer um desses três comandos imprimirá o caractere desejado em um console, desde que o console aceite caracteres UTF-8 (os mais atuais):

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) 

Depois, você pode copiar e colar o glifo real (imagem, caractere) em qualquer editor de texto (habilitado para UTF-8).

Se você precisar ver como esse ponto de código Unicode é codificado em UTF-8, use xxd (visualizador hexadecimal muito melhor 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, no HEX, para evitar erros: 0xE2 0x98 0xA0. Ou seja, os valores entre o espaço (HEX 20) e o Line-Feed (Hex 0A).

Se você deseja aprofundar a conversão de números em caracteres: veja aqui um artigo do wiki de Greg (BashFAQ) sobre a codificação ASCII no Bash!


re: "Ou, no HEX, para evitar erros ..." Eu dificilmente acho que converter um caractere unicode em alguma codificação binária que você expressa em caracteres hexadecimais ajuda a evitar erros. O uso da notação unicode em "bash" evitaria erros, ou seja: "\ uHHHH --- o caractere Unicode (ISO / IEC 10646) cujo valor é o valor hexadecimal ---- HHHH (um a quatro dígitos hexadecimais); \ UHHHHHHHH ---- o caractere Unicode (ISO / IEC 10646), cujo valor é o valor hexadecimal ---- HHHHHHHH (um a oito dígitos hexadecimais)
Astara

4

O printfbuiltin (assim como o coreutils printf) conhece a \usequência de escape que aceita caracteres Unicode de 4 dígitos:

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

Teste com o Bash 4.2.37 (1):

$ printf '\u2620\n'

printf também é um shell embutido. Você provavelmente está usando o macOS bash padrão (v3). Tente com \printfa usar o executável autônomo, ou tentar com atualizado festa
mcint

4

Desculpe por reviver esta pergunta antiga. Porém, ao usar, bashexiste uma abordagem muito fácil para criar pontos de código Unicode a partir da entrada ASCII simples, que nem sequer é bifurcada :

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

Use-o da seguinte maneira para definir certos pontos de código

unicode crossbones 0x2620
echo "$crossbones"

ou despejar os primeiros pontos de código unicode 65536 em stdout (leva menos de 2s na minha máquina. O espaço adicional é para impedir que certos caracteres fluam entre si devido à fonte monoespaçada do shell):

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

ou para contar uma pequena história muito típica dos pais (isso precisa do Unicode 2010):

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

Explicação:

  • printf '\UXXXXXXXX' imprime qualquer caractere Unicode
  • printf '\\U%08x' numberimprime \UXXXXXXXXcom o número convertido em hexadecimal, este é alimentado a outro printfpara realmente imprimir o caractere Unicode
  • printf reconhece octal (0oct), hex (0xHEX) e decimal (0 ou números começando de 1 a 9) como números, para que você possa escolher a representação que melhor se adequar
  • printf -v var ..reúne a saída de printfem uma variável, sem fork (que acelera tremendamente as coisas)
  • local variable existe para não poluir o espaço para nome global
  • local -n var=otheraliases varpara other, de tal forma que a atribuição para varaltera other. Uma parte interessante aqui é que varfaz parte do espaço para nome local, enquanto otherfaz parte do espaço para nome global.
    • Por favor, note que não há tal coisa como localou globalnamespace no bash. As variáveis ​​são mantidas no ambiente e sempre são globais. Local apenas retira o valor atual e o restaura quando a função é deixada novamente. Outras funções chamadas de dentro da função com localainda verão o valor "local". Esse é um conceito fundamentalmente diferente de todas as regras de escopo normais encontradas em outras linguagens (e o que bashfaz é muito poderoso, mas pode levar a erros se você for um programador que não está ciente disso).

bem - não funciona para mim. qualquer tentativa de usar qualquer uma de suas funções, emite: linha 6: local: -n: opção inválida local: use: local name [= value] ... Estou usando o mais recente (10.14.2) MacOS e o bash (GNU bash , versão 3.2.57 (1) -release (x86_64-apple-darwin18))
Motti Shneor 26/03/19

4

Aqui está uma lista de todos os emojis unicode disponíveis:

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

Exemplo:

echo -e "\U1F304"
🌄

Para obter o valor ASCII desse caractere, use hexdump

echo -e "🌄" | hexdump -C

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

E então use os valores informados em formato hexadecimal

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

ecoar a string \ U <hex> não funciona no OSX, apenas gera exatamente o que está entre aspas.
Masukomi


2

Fácil com um liner Python2 / 3:

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

Resulta em:


2

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

Resultado:

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

0

Se o valor hexadecimal do caractere unicode for conhecido

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

Se o valor decimal de um caractere unicode for conhecido

declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.