Bagaimana Anda menggemakan karakter Unicode 4 digit di Bash?

224

Saya ingin menambahkan tengkorak Unicode dan tulang bersilang ke shell prompt saya (khususnya 'TENGKORAK DAN CROSSBON' (U + 2620)), tapi saya tidak tahu mantra sihir untuk membuat gema meludah, atau yang lainnya, Karakter Unicode 4 digit. Yang dua digit itu mudah. Misalnya, gema -e "\ x55",.

Selain jawaban di bawah ini, perlu dicatat bahwa, terminal Anda perlu mendukung Unicode agar hasilnya sesuai dengan yang Anda harapkan. gnome-terminal melakukan tugasnya dengan baik, tetapi ini tidak selalu dihidupkan secara default.

Pada aplikasi Terminal macOS Buka Preferences-> Encodings dan pilih Unicode (UTF-8).

masukomi
sumber
7
Perhatikan bahwa komentar "2 digit mudah" (untuk gema) "Anda hanya berlaku untuk nilai hingga "\x7F"di UTF-8 lokal (yang menurut bashtag ini milik Anda) ... ... pola yang diwakili oleh satu byte tidak pernah berada dalam kisaran \x80-\xFF. Kisaran ini ilegal dalam karakter UTF-8 singl-byte. misalnya nilai Unicode Codepoint U+0080(mis. \x80) sebenarnya 2 byte di UTF-8 .. \xC2\x80..
Peter.O
4
Misalnya printf "\\u007C\\u001C".
kenorb
NB: bagi saya gnome-terminal, echo -e '\ufc'tidak menghasilkan ü, bahkan dengan pengkodean karakter diatur ke UTF-8. Namun, mis. urxvtApakah mencetak mis printf "\\ub07C\\ub01C"seperti yang diharapkan (tidak dengan atau kotak).
isomorphismes
@ Peter.O Mengapa bashtag itu petunjuk yang sangat berguna? Apakah terminal berbeda umum di CJK atau ...?
isomorphismes
1
@ Peter.O zsh, fish, scsh, elvish, dll ... ada banyak shell yang berbeda, masing-masing dapat menangani karakter unicode namun mereka mau (atau tidak). "bash" memperjelas pertanyaan ini bukan tentang cangkang aneh yang melakukan hal berbeda.
masukomi

Jawaban:

237

Dalam UTF-8 sebenarnya 6 digit (atau 3 byte).

$ printf '\xE2\x98\xA0'

Untuk memeriksa bagaimana itu dikodekan oleh konsol, gunakan hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003
vartec
sumber
5
Tambang menghasilkan " " bukannya ☠ ... Mengapa begitu?
trusktr
8
Itu benar. Saya menemukan saya menggunakan LANG=Csebagai gantinya LANG=en_US.UTF-8. Sekarang terminal saya di Gnome menunjukkan simbol dengan benar ... Terminal yang sebenarnya (tty1-6) masih belum.
trusktr
6
Bagi mereka yang mencoba hexdump: 0000000 f0 9f 8d baterjemahkan ke \xf0\x9f\x8d\xba. Contoh gema: echo -e "\xf0\x9f\x8d\xba".
Blaise
8
Anda juga dapat menggunakan $'...'sintaks untuk memasukkan karakter yang dikodekan ke variabel tanpa menggunakan $(...)subkulit menangkap, untuk digunakan dalam konteks yang tidak menafsirkan sendiri urutan pelarian:skull=$'\xE2\x98\xA0'
Andrew Janke
7
Hal lain tentang hexdump: pada mesin saya, perintah kedua di jawaban akan keluar 0000000 98e2 00a0. Tentu saja 0000000ini hanya offset yang tidak penting, tetapi byte setelah diterjemahkan \xe2\x98\xa0, karena mesin menggunakan urutan byte endian kecil.
sigalor
98
% echo -e '\u2620'     # \u takes four hexadecimal digits

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

Ini berfungsi di Zsh (saya sudah memeriksa versi 4.3) dan di Bash 4.2 atau lebih baru.

Juliano
sumber
16
yang baru saja dimuntahkan ketika saya melakukannya.
masukomi
Untuk saya juga. Cangkang mana yang Anda gunakan, Juliano?
Joachim Sauer
2
Maaf, lupa mengatakan bahwa saya menggunakan zsh.
Juliano
32
Dukungan untuk \ u telah ditambahkan di Bash 4.2.
Lri
4
TIDAK bekerja untuk saya, Mac OS 10.14.2, bash (GNU bash, versi 3.2.57 (1) -release (x86_64-apple-darwin18)). Itu hanya mencetak input - $ echo -e '\ u2620' <enter> hanya mencetak: \ u2620
Motti Shneor
68

Selama editor teks Anda dapat mengatasi Unicode (mungkin dikodekan dalam UTF-8) Anda dapat memasukkan titik kode Unicode secara langsung.

Misalnya, dalam editor teks Vim Anda akan masuk ke mode penyisipan dan tekan Ctrl+ V+ Udan kemudian nomor kode-titik sebagai angka heksadesimal 4 digit (pad dengan nol jika perlu). Jadi, Anda akan mengetik Ctrl+ V+U 2 6 2 0 . Lihat: Apa cara termudah untuk memasukkan karakter Unicode ke dalam dokumen?

Di terminal yang menjalankan Bash Anda akan mengetik CTRL+SHIFT + Udan mengetikkan kode-heksadesimal karakter yang Anda inginkan. Selama input, kursor Anda harus menunjukkan garis bawah u. Non-digit pertama yang Anda ketikkan input berakhir, dan merender karakter. Jadi, Anda dapat mencetak U + 2620 di Bash menggunakan yang berikut:

echo CTRL+ SHIFT+U2620ENTERENTER

(Masukan pertama mengakhiri input Unicode, dan yang kedua menjalankan echoperintah.)

Kredit: Tanyakan Ubuntu SE

RobM
sumber
1
Sumber yang baik untuk poin kode hexademical
RobM
1
Versi vim yang saya gunakan (7.2.411 pada RHEL 6.3) tidak merespons seperti yang diinginkan ketika ada titik di antara ctrl-v dan u, tetapi berfungsi dengan baik ketika titik tersebut dihilangkan.
Chris Johnson
@ ChrisJohnson: Saya telah menghapus periode dari instruksi, itu tidak dimaksudkan untuk menjadi tombol tekan (itulah sebabnya tidak muncul dengan efek keyboard). Maaf bila membingungkan.
RobM
5
Hati-hati: ini berfungsi di terminal yang menjalankan Bash hanya jika Anda menjalankannya di lingkungan GTK + , seperti Gnome.
nr
1
Kemampuan untuk C-S-u 2 6 2 0adalah fitur emulator terminal Anda, Metode Input X (XIM), atau yang serupa. AFAIK, Anda tidak akan dapat mengirim keduanya SHIFTdan CTRLke lapisan terminal. Terminal hanya berbicara dalam karakter, bukan dalam keyyms dan kode kunci seperti server X Anda (juga, itu adalah 7-bit untuk semua maksud dan tujuan). Di dunia ini, CTRLtopeng 4 bit paling signifikan (& 0b00001111) yang menghasilkan
nabin-info
31

Berikut ini adalah implementasi Bash sepenuhnya internal, tidak ada forking, ukuran tak terbatas dari karakter 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

Output adalah:

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

Masukkan "☠" ke dalam skrip shell Anda. Di tempat yang benar dan di konsol yang mendukung Unicode, ia akan mencetak dengan baik:

$ echo 

$

"Pemecahan masalah" yang jelek akan menghasilkan urutan UTF-8, tetapi itu juga tergantung pada pengkodean yang digunakan:

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

$
Joachim Sauer
sumber
13

Satu garis cepat untuk mengkonversi karakter UTF-8 ke dalam format 3-byte:

var="$(echo -n '☠' | od -An -tx1)"; printf '\\x%s' ${var^^}; echo
David King
sumber
5
Saya tidak akan menyebut contoh di atas dengan cepat (dengan 11 perintah dan paramsnya) ... Juga hanya menangani 3 byte karakter UTF-8 (karakter UTF-8 dapat 1, 2, atau 3 byte) ... Ini sedikit lebih pendek dan bekerja untuk 1-3 ++++ byte: printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... xxd dikirimkan sebagai bagian dari paket 'vim-common'
Peter.O
PS: Saya baru saja memperhatikan bahwa contoh hexdump / awk di atas adalah swithching urutan byte dalam byte-pair. Ini tidak berlaku untuk dump UTF-8. Akan relavent jika itu adalah dump UTF-16LE dan ingin menampilkan Unicode Codepoints , tetapi di sini tidak masuk akal karena inputnya adalah UTF-8 dan outputnya persis seperti input (ditambah \ x sebelum setiap hexdigit -pair)
Peter.O
7
Karakter UTF-8 dapat urutan 1 - 4 byte
cms
1
berdasarkan komentar @ Peter.O, saya menemukan yang berikut, sementara lebih besar, cukup berguna: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
Astaga, manusia. Pertimbangkan: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ... selamat menikmati 👍
nabin-info
8

Saya menggunakan ini:

$ echo -e '\u2620'

Ini lebih mudah daripada mencari representasi hex ... Saya menggunakan ini dalam skrip shell saya. Itu bekerja pada AFAIK istilah-gnome dan urxvt.

Metal3d
sumber
2
@masukomi jika Anda tahu cara menggunakan minuman, Anda dapat menginstal bash yang lebih baru dan menggunakannya. Di atas berfungsi dengan baik di terminal mac saya ketika menggunakan bash yang ditingkatkan.
mcheema
Ya, tidak masalah dengan versi bash yang lebih baru. Hower string cepat, mis. $ PS1 tidak menggunakan format gema escape
cms
6

Anda mungkin perlu menyandikan titik kode sebagai oktal agar ekspansi yang cepat dapat mendekodekannya dengan benar.

U + 2620 disandikan sebagai UTF-8 adalah E2 98 A0.

Jadi di Bash,

export PS1="\342\230\240"

akan membuat cangkang Anda menjadi tengkorak dan tulang.

cm
sumber
hai, kode apa yang harus saya masukkan untuk "e0 b6 85"? bagaimana saya bisa menemukannya?
Udayantha Udy Warnasuriya
konversikan saja angka heksadesimal (basis 16) e0 b6 85 menjadi oktal (basis 8) - menggunakan kalkulator mungkin merupakan cara termudah untuk melakukan ini
cms
e0 b6 85 hex adalah 340 266 205 oktal
cm
Ini berhasil, terima kasih banyak! Dan btw, Anda dapat findal oktal versi di halaman ini: graphemica.com/%E2%9B%B5
Perlnika
6

Dalam bash untuk mencetak karakter Unicode ke output gunakan \ x, \ u atau \ U (pertama untuk 2 digit hex, kedua untuk hex 4 digit, ketiga untuk panjang apa pun)

echo -e '\U1f602'

Saya ingin menetapkannya ke variabel, gunakan sintaks $ '...'

x=$'\U1f602'
echo $x
pengguna2622016
sumber
5

Jika Anda tidak keberatan dengan Perl one-liner:

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

-CSmemungkinkan UTF-8 decoding pada input dan encoding UTF-8 pada output. -Emengevaluasi argumen berikutnya sebagai Perl, dengan fitur-fitur modern seperti saydiaktifkan. Jika Anda tidak ingin baris baru di bagian akhir, gunakan printsebagai ganti say.

Flimm
sumber
5

Salah satu dari tiga perintah ini akan mencetak karakter yang Anda inginkan di konsol, asalkan konsol tersebut menerima karakter UTF-8 (sebagian besar saat ini melakukannya):

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) 

Setelah itu, Anda dapat menyalin dan menempelkan mesin terbang (gambar, karakter) yang sebenarnya ke editor teks apa pun (yang diaktifkan UTF-8).

Jika Anda perlu melihat bagaimana Unicode Code Point dikodekan dalam UTF-8, gunakan xxd (hex viewer yang jauh lebih baik daripada 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

Atau, dalam HEX untuk menghindari kesalahan: 0xE2 0x98 0xA0. Yaitu, nilai-nilai antara spasi (HEX 20) dan Line-Feed (Hex 0A).

Jika Anda ingin terjun lebih dalam ke konversi angka menjadi karakter: lihat di sini untuk melihat artikel dari wiki Greg (BashFAQ) tentang pengkodean ASCII di Bash!

user2350426
sumber
re: "Atau, di HEX untuk menghindari kesalahan ..." Saya hampir tidak berpikir bahwa mengubah unicode char ke beberapa pengkodean biner yang Anda ekspresikan dalam hex chars, membantu menghindari kesalahan. Menggunakan notasi unicode dalam "bash" akan lebih baik menghindari kesalahan yaitu: "\ uHHHH --- the Unicode (ISO / IEC 10646) karakter yang nilainya adalah ---- nilai heksadesimal HHHH (satu hingga empat digit hex); \ UHHHHHHHHH ---- the Unicode (ISO / IEC 10646) karakter yang nilainya adalah nilai heksadesimal ---- HHHHHHHHH (satu hingga delapan digit hex)
Astara
4

The printfbuiltin (hanya sebagai coreutils' printf) tahu \uurutan escape yang menerima 4 digit Unicode karakter:

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

Tes dengan Bash 4.2.37 (1):

$ printf '\u2620\n'
Michael Jaros
sumber
printf juga merupakan shell bawaan. Anda mungkin menggunakan macOS bash (v3) default. Coba dengan \printfmenggunakan executable mandiri, atau coba dengan upgrade bash
mcint
4

Maaf telah menghidupkan kembali pertanyaan lama ini. Tetapi ketika menggunakan bashada pendekatan yang sangat mudah untuk membuat Unicode codepoint dari input ASCII biasa, yang bahkan tidak bercabang sama sekali:

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

Gunakan sebagai berikut untuk menentukan titik-titik kode tertentu

unicode crossbones 0x2620
echo "$crossbones"

atau untuk membuang 65536 unicode codepoints ke stdout (membutuhkan waktu kurang dari 2 pada mesin saya. Ruang tambahan adalah untuk mencegah karakter tertentu saling mengalir karena font monospace shell):

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

atau untuk menceritakan kisah orang tua yang sangat tipikal (ini membutuhkan Unicode 2010):

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

Penjelasan:

  • printf '\UXXXXXXXX' mencetak karakter Unicode
  • printf '\\U%08x' numbermencetak \UXXXXXXXXdengan nomor yang dikonversi ke Hex, ini kemudian diumpankan ke yang lainprintf untuk benar-benar mencetak karakter Unicode
  • printf mengenali oktal (0oct), hex (0xHEX) dan desimal (0 atau angka dimulai dengan 1 hingga 9) sebagai angka, sehingga Anda dapat memilih representasi mana yang paling cocok
  • printf -v var ..mengumpulkan keluaran printfmenjadi variabel, tanpa garpu (yang sangat mempercepat hal-hal)
  • local variable ada di sana untuk tidak mencemari namespace global
  • local -n var=otheralias varuntuk other, tugas seperti itu untuk varalter other. Satu bagian yang menarik di sini adalah, itu varadalah bagian dari namespace lokal, sementaraother adalah bagian dari namespace global.
    • Harap perhatikan bahwa tidak ada yang namanya localatau globalnamespace di bash. Variabel disimpan di lingkungan, dan itu selalu bersifat global. Lokal hanya membuang nilai saat ini dan mengembalikannya ketika fungsi dibiarkan lagi. Fungsi-fungsi lain yang dipanggil dari dalam fungsi dengan localmasih akan melihat nilai "lokal". Ini adalah konsep yang secara fundamental berbeda dari semua aturan pelingkupan normal yang ditemukan dalam bahasa lain (dan apa yang bashdilakukannya sangat kuat tetapi dapat menyebabkan kesalahan jika Anda seorang programmer yang tidak menyadarinya).
Tino
sumber
baik - tidak bekerja sama sekali untuk saya. setiap upaya untuk menggunakan salah satu fungsi Anda, pancarkan: baris 6: lokal: -n: opsi lokal tidak valid: penggunaan: nama lokal [= nilai] ... Saya menggunakan MacOS dan bash (GNU bash) terbaru (10.14.2) , versi 3.2.57 (1) -release (x86_64-apple-darwin18))
Motti Shneor
4

Berikut adalah daftar semua emoji unicode yang tersedia:

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

Contoh:

echo -e "\U1F304"
🌄

Untuk mendapatkan nilai ASCII dari karakter ini gunakan hexdump

echo -e "🌄" | hexdump -C

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

Dan kemudian gunakan nilai-nilai yang diinformasikan dalam format hex

echo -e "\xF0\x9F\x8C\x84\x0A"
🌄
Matheus
sumber
menggemakan string \ U <hex> tidak berfungsi pada OSX, itu hanya menampilkan persis apa yang ada dalam tanda kutip.
masukomi
2

Mudah dengan Python2 / 3 one-liner:

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

Hasil dalam:

Chris Johnson
sumber
2

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

Keluaran:

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

Jika nilai hex karakter unicode diketahui

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

Jika nilai desimal karakter unicode diketahui

declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"
philcolbourn
sumber