如何在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个回答

283

在UTF-8中,实际上有6个数字(或3个字节)。

$ printf '\xE2\x98\xA0'

使用hexdump命令可以查看控制台对其进行了哪种编码:

$ printf ☠ | hexdump
0000000 98e2 00a0                              
0000003

7
为什么我的矿机输出的是“���”,而不是☠? - trusktr
9
没错。我发现我使用的是 LANG=C 而不是 LANG=en_US.UTF-8。现在我的 Gnome 终端可以正确显示符号了……但真正的终端(tty1-6)仍然不能正常显示。 - trusktr
6
对于那些尝试进行十六进制转储的人:0000000 f0 9f 8d ba 的翻译是 \xf0\x9f\x8d\xba。一个例子是 echo -e "\xf0\x9f\x8d\xba" - Blaise
12
您还可以使用$'...'语法将编码字符存储到变量中,而无需使用$(...)捕获子shell,以便在不解释转义序列的上下文中使用:skull=$'\xE2\x98\xA0' - Andrew Janke
7
关于hexdump的另一件事:在我的机器上,答案中的第二个命令输出0000000 98e2 00a0。当然,0000000只是一个不重要的偏移量,但它之后的字节转换为\xe2\x98\xa0,因为该机器使用小端字节顺序。 - sigalor
显示剩余5条评论

177
% echo -e '\u2620'     # \u takes four hexadecimal digits
☠
% echo -e '\U0001f602' # \U takes eight hexadecimal digits

这个在Zsh中可行(我已经检查了4.3版本),以及Bash 4.2或更新的版本中也可以。


23
当我这样做时,它只会呕出\u2620。 - masukomi
2
抱歉,我忘了说我使用的是zsh。 - Juliano
39
Bash 4.2 版本新增了对 \u 的支持。 - Lri
5
有一种使用 ANSI 字符串的版本,可以这样写:echo $'\U1f602' - memoselyk
6
对我来说无效,在Mac OS 10.14.2上使用bash(GNU bash,版本3.2.57(1)-release(x86_64-apple-darwin18))。它只是打印出输入 - $ echo -e'\u2620' <enter> 只会简单地打印出:\u2620。 - Motti Shneor
显示剩余4条评论

75
只要你的文本编辑器能够处理Unicode(假定以UTF-8编码),你可以直接输入Unicode代码点。
例如,在Vim文本编辑器中,您需要进入插入模式并按下Ctrl+V+U,然后输入4位十六进制数字作为代码点号(如有必要,请使用零进行填充)。因此,您需要键入Ctrl+V+U 2 6 2 0。参见:什么是将Unicode字符插入文档的最简单方法? 在运行Bash的终端中,您需要键入CTRL+SHIFT+U,然后键入所需字符的十六进制代码点。在输入期间,您的光标应该显示一个带有下划线的u。您键入的第一个非数字字符将结束输入并呈现字符。因此,您可以使用以下命令在Bash中打印U+2620: echo CTRL+SHIFT+U2620ENTERENTER

第一个回车键结束Unicode输入,第二个运行echo命令。

来源:Ask Ubuntu SE


2
一个好的十六进制代码点来源是http://unicodelookup.com/#0x2620/1 - RobM
1
жҲ‘дҪҝз”Ёзҡ„vimзүҲжң¬пјҲRHEL 6.3дёҠзҡ„7.2.411пјүеңЁctrl-vе’Ңuд№Ӣй—ҙжңүдёҖдёӘзӮ№ж—¶ж— жі•жҢүйў„жңҹе“Қеә”пјҢдҪҶжҳҜеҪ“зңҒз•ҘиҜҘзӮ№ж—¶еҸҜд»ҘжӯЈеёёе·ҘдҪңгҖӮ - Chris Johnson
@ChrisJohnson:我已经从说明中删除了句号,它不是预期的按键(这就是为什么它没有出现在键盘效果中)。对于造成的混淆,非常抱歉。 - RobM
6
注意:仅在运行于GTK+环境下(如Gnome)的Bash终端中才能生效。请当心。 - n.r.
2
C-S-u 2 6 2 0 的能力是您的终端仿真器、X 输入法 (XIM) 或类似工具的一个功能。据我所知,您将无法同时发送 SHIFTCTRL 到终端层。终端只能以字符形式进行通信,而不像您的 X 服务器那样使用键符和键码(此外,它在所有意义上都是 7 位)。在这个世界里,CTRL 掩盖了最高的 4 位 (& 0b00001111),这导致... - nabin-info
在vim中,它并不是一次性按下 ctrl + v + u。而是先按下 ctrl-v 然后再按下 u。小写字母 u 后跟最多四个十六进制数字(例如 03bb,λ),大写字母 U 后跟8个十六进制数字(例如0001f4a9)。 - Fernando Basso

37
这是一个完全内部实现的Bash,没有分叉,支持无限大小的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

输出结果如下:

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

我非常好奇这种迂回方法的原因,以及 REPLY 变量的具体用途。我假设您检查了 bash 源代码或运行了某些内容进行优化,我可以看出您的选择可能是优化的,尽管高度依赖于解释器。 - nabin-info
@nabin-info "$REPLY"在这种情况下是错误的选择,使用小写字母作为变量名可能更好,以避免误解。您可以尝试替换sed 's/REPLY/anyVarname/g',脚本仍然会正常工作。 - F. Hauri - Give Up GitHub
@nabin-info $REPLY 在这种情况下是一个错误的选择,最好使用小写字母来命名变量,以避免误解。你可以尝试替换 sed 's/REPLY/anyVarname/g',脚本仍然可以正常工作。 - undefined
$REPLY是内置的read命令的默认输出变量。这个解决方案不使用read命令,但$REPLY和任何名称一样好用。我的观察是,在Bash脚本中,全大写的变量名通常表示“外部”事物,无论是配置选项/全局变量、环境变量,还是其他命令或函数的返回值。小写变量倾向于在局部使用。这纯粹是我的意见,你可以使用任何变量名。 - Orwellophile
$REPLY是内置的read命令的默认输出变量。这个解决方案不使用read命令,但是$REPLY和任何其他名称一样好用。我观察到,在bash脚本中,全大写的变量名通常表示'外部'事物,无论是配置选项/全局变量、环境变量,还是其他命令或函数的返回值。小写变量通常用于局部使用。这只是我的个人观点,你可以使用任何你喜欢的变量名。 - undefined

26

将UTF-8字符转换为它们的3字节格式的快速一行代码:

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

两者的输出都是\xE2\x98\xA0,因此您可以反向编写:

echo $'\xe2\x98\xa0'   # ☠

6
以上示例我不会称之为“快速”(它有11个命令和它们的参数)......另外,它只能处理3字节的UTF-8字符(UTF-8字符可以是1、2或3个字节)...这是一个比较短的方法,适用于1到3++++字节:printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... xxd作为“vim-common”软件包的一部分提供。 - Peter.O
8
UTF-8 字符可以由 1 到 4 个字节组成。 - cms
1
基于@Peter.O的评论,我发现以下代码虽然更长,但非常方便: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 } # 使用方法: $ hexFromGlyph ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF $ hexFromGlyph -n ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF - StephaneAG
6
老天啊,考虑一下:codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ z...享受吧。 - nabin-info
@nabin-info 太好了,我正想问一下如何获取utf-16表示,你的codepoints函数就可以做到。谢谢。 - erik
显示剩余2条评论

14

只需在您的shell脚本中输入"☠"。在正确的语言环境和启用Unicode的控制台上,它会正常打印:

$ echo ☠
☠
$

一个丑陋的“解决办法”是输出UTF-8序列,但这也取决于所使用的编码方式:

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

12
以下是所有可用的Unicode表情符号列表: https://en.wikipedia.org/wiki/Emoji#Unicode_blocks 示例:
echo -e "\U1F304"


要获取此字符的ASCII值,请使用hexdump。
echo -e "" | hexdump -C

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

然后使用以十六进制格式提供的值

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


1
在OSX上回显\U<hex>字符串无效,它只会输出引号中的内容。 - masukomi
macOS 上默认的 bash 版本(对我来说是 3.2.57)早于 Unicode 功能。请更新 bash 或使用 zsh。 - Quantum7
将其他普通字符添加的正确方法是使用空字符吗?echo -e '\U2192\0abc' - Pablo Bianchi

10
在Bash中,要打印Unicode字符到输出,请使用\x、\u或\U(第一个是2位十六进制数,第二个是4位十六进制数,第三个可以是任意长度)。
echo -e '\U1f602'

如果您想将其分配给变量,请使用$'...'语法

x=$'\U1f602'
echo $x

9

只要控制台接受UTF-8字符(现在大多数控制台都支持),这三个命令中的任何一个都可以在控制台中打印您想要的字符:

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) ☠

接下来,您可以将实际的字形(图像、字符)复制并粘贴到任何(启用UTF-8)文本编辑器中。

如果您需要查看这种Unicode代码点如何在UTF-8中编码,请使用xxd(比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

或者,为避免错误,使用十六进制表示:0xE2 0x98 0xA0。也就是说,这些值在空格(HEX 20)和换行符(Hex 0A)之间。
如果您想深入了解将数字转换为字符,请查看Greg的维基百科(BashFAQ)中有关Bash中ASCII编码的文章:点击这里

我认为将Unicode字符转换为某些用十六进制字符表示的二进制编码并不能帮助避免错误。在“bash”中使用Unicode符号更能避免错误,即: \uHHHH---值为十六进制值HHHH(一到四个十六进制数字)的Unicode(ISO/IEC 10646)字符; \UHHHHHHHH---值为十六进制值HHHHHHHH(一到八个十六进制数字)的Unicode(ISO/IEC 10646)字符。 - Astara

7
我正在使用这个:

$ echo -e '\u2620'

这比搜索十六进制表示法要容易得多...我在我的shell脚本中使用它。据我所知,这适用于gnome-term和urxvt。

2
@masukomi 如果你知道如何使用brew,你可以安装一个更新的bash并使用它。当使用升级后的bash时,上述命令在我的Mac终端上运行良好。 - mcheema
没问题,对于较新版本的bash来说是可以的。但是提示字符串,例如$PS1不使用echo转义格式。 - cms

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