如何在Bash中回显一个4位Unicode字符?

271

我想在我的Shell提示符中添加Unicode骷髅和十字骨头符号(特别是'SKULL AND CROSSBONES' (U+2620)),但我无法找到使echo输出它或任何其他4位Unicode字符的方法。两位数的字符很容易打出来。例如:echo -e "\x55"

除了下面的答案之外,还应该注意,显然,您的终端需要支持Unicode才能得到您期望的输出。gnome-terminal在这方面做得很好,但它不一定默认开启。

在macOS的终端应用程序中,转到“首选项->编码”并选择Unicode(UTF-8)。


7
请注意,你的“两位数字容易(回声)”评论仅适用于在 UTF-8 区域设置中(这是 bash 标签建议您使用的区域设置)值高达 "\x7F" 的情况...由一个 单字节 表示的模式永远不会在范围 \x80-\xFF 内。这个范围在单字节 UTF-8 字符中是非法的。例如,Unicode 代码点值为 U+0080(即 \x80)实际上在 UTF-8 中是两个字节..\xC2\x80 - Peter.O
6
请将文本从英语翻译成中文。只返回已翻译的文本:例如printf "\\u007C\\u001C" - kenorb
注意:对于我在 gnome-terminal 中使用 echo -e '\ufc' 并不能产生一个ü,即使字符编码设置为UTF-8。然而,例如 urxvt 正常打印输出,如 printf "\\ub07C\\ub01C"(不会显示为�或方框)。 - isomorphismes
@Peter.O 为什么“bash”标签是如此有用的提示?CJK地区常见不同的终端吗? - isomorphismes
2
@Peter.O zsh、fish、scsh、elvish等等,有许多不同的shell,每个都可以按照自己的方式处理Unicode字符(或者不处理)。"bash"明确了这个问题不是关于某种奇怪的shell以不同的方式做事情。 - masukomi
while read -r line; do echo -e "$line"; done - kenorb
19个回答

6
如果您不介意使用Perl的一行代码,可以尝试以下方法:
$ perl -CS -E 'say "\x{2620}"'

-CS 使输入进行UTF-8解码,并在输出时进行UTF-8编码。 -E 将下一个参数作为Perl进行评估,启用像say这样的现代特性。如果您不想在末尾换行,请使用print而不是say


6

为了使提示扩展正确解码代码点,您可能需要将其作为八进制编码。

将U+2620编码为UTF-8的结果为E2 98 A0。

因此在Bash中,

export PS1="\342\230\240"

这个命令会将你的shell提示符变成骷髅头和骨头。


嗨,我应该输入什么代码来表示“e0 b6 85”?我该如何找到它? - Udy Warnasuriya
将十六进制数(基数为16)e0 b6 85转换为八进制数(基数为8)- 使用计算器可能是最简单的方法。 - cms
e0 b6 85 十六进制是 340 266 205 八进制。 - cms
这个可行,非常感谢!顺便说一下,你可以在这些页面上找到八进制版本:http://graphemica.com/%E2%9B%B5 - Perlnika

5

很抱歉重新提出这个老问题。但是,当使用时,有一种非常简单的方法可以从普通的ASCII输入创建Unicode代码点,甚至根本不需要分叉

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

使用方法如下,以定义特定的代码点:
unicode crossbones 0x2620
echo "$crossbones"

或者将前65536个unicode代码点转储到标准输出(stdout)(在我的机器上只需要不到2秒的时间。额外的空间是为了防止由于shell的等宽字体而导致某些字符流入彼此):

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

或者讲一个非常典型的家长故事(这需要Unicode 2010):
unicodes 0x1F6BC 32 43 32 0x1F62D 32 32 43 32 0x1F37C 32 61 32 0x263A 32 32 43 32 0x1F4A9 10

解释:

  • printf '\UXXXXXXXX' 打印任何Unicode字符
  • printf '\\U%08x' number 用数字转换为十六进制,打印出\UXXXXXXXX,然后将其输入到另一个printf中实际打印出Unicode字符
  • printf 可以识别八进制(0oct)、十六进制(0xHEX)和十进制(0或以1到9开头的数字)作为数字,因此可以选择最适合的表示方法
  • printf -v var ..printf的输出聚集到变量中,不需要fork(分叉)(这会极大地加快速度)
  • local variable 是为了不污染全局命名空间
  • local -n var=othervarother别名,这样对var的赋值会改变other。 这里有一个有趣的部分,即var是局部命名空间的一部分,而other是全局命名空间的一部分。
    • 请注意,在bash中没有localglobal命名空间。 变量保存在环境中,因此始终是全局的。 local只是将当前值放在一边,并在离开函数时恢复它。 从函数内部调用带有local的其他函数仍将看到“本地”值。 这是与其他语言中找到的所有正常作用域规则根本不同的概念(bash所做的非常强大,但如果您不知道这一点,则可能会导致错误)。

对我来说完全不起作用。任何尝试使用您的任何函数都会发出:line 6: local: -n: 无效选项 local: usage: local name[=value] ... 我正在使用最新的(10.14.2)MacOS和bash(GNU bash,版本3.2.57(1)-release(x86_64-apple-darwin18))。 - Motti Shneor

5

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

输出:

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

5

升级2023...

从一段时间以前开始,printf中使用了%b

printf %b\\n \\U1F600


所以你可以使用printf内置命令的-v标志来分配一个变量:
printf -v smiley \\U1F600
echo $smiley 


然后快速显示一部分Unicode表的内容:
printf %b\\n \\U1F6{{0..9},{A..F}}{{0..9},{a..f}}|paste -d\  -{,,,}{,,,}
               
               
               
               
               
               
               
               
               
               
               
               
               
               
               
               

显示盲文部分:
printf %b\\n \\U28{{0..9},{A..F}}{{0..9},{a..f}}|paste -d\  -{,,,}{,,,}
⠀ ⠁ ⠂ ⠃ ⠄ ⠅ ⠆ ⠇ ⠈ ⠉ ⠊ ⠋ ⠌ ⠍ ⠎ ⠏
⠐ ⠑ ⠒ ⠓ ⠔ ⠕ ⠖ ⠗ ⠘ ⠙ ⠚ ⠛ ⠜ ⠝ ⠞ ⠟
⠠ ⠡ ⠢ ⠣ ⠤ ⠥ ⠦ ⠧ ⠨ ⠩ ⠪ ⠫ ⠬ ⠭ ⠮ ⠯
⠰ ⠱ ⠲ ⠳ ⠴ ⠵ ⠶ ⠷ ⠸ ⠹ ⠺ ⠻ ⠼ ⠽ ⠾ ⠿
⡀ ⡁ ⡂ ⡃ ⡄ ⡅ ⡆ ⡇ ⡈ ⡉ ⡊ ⡋ ⡌ ⡍ ⡎ ⡏
⡐ ⡑ ⡒ ⡓ ⡔ ⡕ ⡖ ⡗ ⡘ ⡙ ⡚ ⡛ ⡜ ⡝ ⡞ ⡟
⡠ ⡡ ⡢ ⡣ ⡤ ⡥ ⡦ ⡧ ⡨ ⡩ ⡪ ⡫ ⡬ ⡭ ⡮ ⡯
⡰ ⡱ ⡲ ⡳ ⡴ ⡵ ⡶ ⡷ ⡸ ⡹ ⡺ ⡻ ⡼ ⡽ ⡾ ⡿
⢀ ⢁ ⢂ ⢃ ⢄ ⢅ ⢆ ⢇ ⢈ ⢉ ⢊ ⢋ ⢌ ⢍ ⢎ ⢏
⢐ ⢑ ⢒ ⢓ ⢔ ⢕ ⢖ ⢗ ⢘ ⢙ ⢚ ⢛ ⢜ ⢝ ⢞ ⢟
⢠ ⢡ ⢢ ⢣ ⢤ ⢥ ⢦ ⢧ ⢨ ⢩ ⢪ ⢫ ⢬ ⢭ ⢮ ⢯
⢰ ⢱ ⢲ ⢳ ⢴ ⢵ ⢶ ⢷ ⢸ ⢹ ⢺ ⢻ ⢼ ⢽ ⢾ ⢿
⣀ ⣁ ⣂ ⣃ ⣄ ⣅ ⣆ ⣇ ⣈ ⣉ ⣊ ⣋ ⣌ ⣍ ⣎ ⣏
⣐ ⣑ ⣒ ⣓ ⣔ ⣕ ⣖ ⣗ ⣘ ⣙ ⣚ ⣛ ⣜ ⣝ ⣞ ⣟
⣠ ⣡ ⣢ ⣣ ⣤ ⣥ ⣦ ⣧ ⣨ ⣩ ⣪ ⣫ ⣬ ⣭ ⣮ ⣯
⣰ ⣱ ⣲ ⣳ ⣴ ⣵ ⣶ ⣷ ⣸ ⣹ ⣺ ⣻ ⣼ ⣽ ⣾ ⣿

更好地转化为一个小函数
showU8_256() { 
    local i a
    for a ;do
        for i in {0..9} {A..F}; do
            printf '\\U%05Xx: %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b\n' \
                0x$a$i \\U$a${i}{{0..9},{A..F}}
        done
    done
}

那么

showU8_256 1f{3,4}
\U01F30x:                
\U01F31x:                
\U01F32x:                
\U01F33x:                
\U01F34x:                
\U01F35x:                
\U01F36x:                
\U01F37x:                
\U01F38x:                
\U01F39x:                
\U01F3Ax:                
\U01F3Bx:                
\U01F3Cx:                
\U01F3Dx:                
\U01F3Ex:                
\U01F3Fx:                
\U01F40x:                
\U01F41x:                
\U01F42x:                
\U01F43x:                
\U01F44x:                
\U01F45x:                
\U01F46x:                
\U01F47x:                
\U01F48x:                
\U01F49x:                
\U01F4Ax:                
\U01F4Bx:                
\U01F4Cx:                
\U01F4Dx:                
\U01F4Ex:                
\U01F4Fx:                

浏览Unicode表

为了这个目的,在寻找可靠的方法后,我最终在SuperUser上发布了转储/浏览完整的Unicode表,我的 dumpUnicode脚本:

./dumpUnicode | grep SMIL.*SUNGLAS\\\|FONDUE
\U01F60E: '' SMILING FACE WITH SUNGLASSES
\U01FAD5: '' FONDUE

4

printf 命令(与 coreutils 的 printf 命令相同)支持 \u 转义字符序列,可以接受 4 位 Unicode 字符:

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

使用Bash 4.2.37(1)进行测试:

$ printf '\u2620\n'

printf 也是一个 shell 内置命令。你可能正在使用默认的 macOS bash(v3)。尝试使用 \printf 来使用独立的可执行文件,或者尝试升级 bash。 - mcint

3

2

使用Python2/3的一行代码很容易实现:

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

结果是:


1
如果已知 Unicode 字符的十六进制值
H="2620"
printf "%b" "\u$H"

如果已知Unicode字符的十进制值
declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接