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


224

我想将Unicode骷髅和交叉骨添加到我的shell提示符中(特别是“ SKULL AND CROSSBONES”(U + 2620)),但我无法弄清楚使回声吐出的魔咒,或其他任何东西, 4位Unicode字符。两位数字很容易。例如,echo -e“ \ x55”,。

除了以下答案外,还应注意,很明显,您的终端需要支持Unicode才能使输出符合您的期望。gnome-terminal在这方面做得很好,但是默认情况下不一定打开它。

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


7
请注意,您的“ 2位数字很容易(回显)”注释仅对"\x7F"UTF-8语言环境中的值有效 (该bash标记建议您使用该语言)... 单字节表示的模式永远不在此范围内\x80-\xFF。此范围在单字节UTF-8字符中是非法的。例如一个Unicode码点值U+0080(即,\x80)实际上2个字节是UTF-8 .. \xC2\x80..
Peter.O

4
例如printf "\\u007C\\u001C"
kenorb '16

注意:对我来说,即使将字符编码设置为UTF-8 gnome-terminalecho -e '\ufc'也不会生成ü。但是,例如按预期进行urxvt打印printf "\\ub07C\\ub01C"(不带有或框)。
同构性

@ Peter.O为什么bash标记这么有用?在CJK或…中是否使用不同的端子?
同构

1
@ Peter.O zsh,fish,scsh,elvish等...有很多不同的shell,每个shell都可以处理所需的Unicode字符(或不需要)。“ bash”清楚地表明了这个问题不是关于一些奇怪的外壳,它们的功能有所不同。
masukomi

Answers:


237

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

$ printf '\xE2\x98\xA0'

要检查控制台的编码方式,请使用hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003

5
我的输出是“ ...”而不是☠...为什么?
trusktr 2012年

8
确实如此。我发现我使用LANG=C而不是LANG=en_US.UTF-8。现在我在Gnome中的终端正确显示了符号...真正的终端(tty1-6)仍然没有。
trusktr 2012年

6
对于那些尝试十六进制转储的人:0000000 f0 9f 8d ba转换为\xf0\x9f\x8d\xba。回声示例:echo -e "\xf0\x9f\x8d\xba"
布莱斯2015年

8
您还可以使用$'...'语法将编码后的字符输入变量中,而无需使用$(...)捕获子外壳程序,以用于本身无法解释转义序列的上下文中:skull=$'\xE2\x98\xA0'
Andrew Janke 2015年

7
关于hexdump的另一件事:在我的机器上,答案输出中的第二个命令0000000 98e2 00a0。当然,这0000000只是一个不重要的偏移量,但是它后面的字节转换为\xe2\x98\xa0,因为机器使用小尾数字节顺序。
sigalor '16

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

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

这在Zsh(我已经检查过版本4.3)和Bash 4.2或更高版本中有效。


16
当我这样做时,它只会吐出\ u2620。
masukomi

我也是 Juliano,您使用的是哪个外壳?
Joachim Sauer,2009年

2
抱歉,忘了说我使用zsh。
朱利诺2009年

32
在Bash 4.2中添加了对\ u的支持。
Lri 2012

4
不适用于我,Mac OS 10.14.2,bash(GNU bash,版本3.2.57(1)-发行版(x86_64-apple-darwin18))。它只是打印出输入-$ echo -e'\ u2620'<enter>简单地打印出:\ u2620
Motti Shneor

68

只要您的文本编辑者可以处理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



1
当ctrl-v和u之间有一个点时,我正在使用的vim版本(在RHEL 6.3上为7.2.411)未按要求进行响应,但是当省略该点时,效果很好。
克里斯·约翰逊

@ChrisJohnson:我已经从说明中删除了句号,这并不是要用作按键(这就是为什么它没有键盘效果出现的原因)。对困惑感到抱歉。
RobM 2013年

5
当心:仅当您在GTK +环境下(例如Gnome)运行Bash时,此方法才能在运行Bash的终端中使用。
2014年

1
的能力C-S-u 2 6 2 0是你的终端仿真器,X输入方法(XIM),或类似的特征。AFAIK,您将无法同时发送SHIFTCTRL到终端层。终端只用字符说话,而不用像X服务器那样的键盘符号和键码说话(同样,出于所有目的和目的,它是7位的)。在这个世界上,CTRL掩盖了4个最高有效位(&0b00001111),从而导致
nabin-info

31

这是一个完全内部的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

14

只需在您的shell脚本中输入“☠”即可。在正确的语言环境中,并在启用Unicode的控制台上,它可以正常打印:

$ echo 

$

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

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

$

13

快速一线将UTF-8字符转换为3字节格式:

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

5
我不会快速调用上面的示例(带有11个命令及其参数)...而且它仅处理3字节的UTF-8字符`(UTF-8字符可以为1、2或3字节)...这是短一点的,并且适用于1-3 ++++字节:printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... xxd作为“ vim-common”软件包的一部分提供
Peter.O 2011年

PS:我刚刚注意到上面的hexdump / awk示例正在将字节对的字节序列切换。这并不适用于UTF-8转储。如果它是UTF-16LE的转储并想输出Unicode Codepoints,那将是相对的,但是在这里没有意义,因为输入是UTF-8,输出完全是输入(加上每个十六进制数字前的\ x -pair)
Peter.O

7
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 } # usage: $ hexFromGlyph ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF $ hexFromGlyph -n ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF
StephaneAG

2
天哪 试想一下: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ...享受👍
nabin-信息

8

我正在使用这个:

$ echo -e '\u2620'

这比搜索十六进制表示形式要容易得多...我在shell脚本中使用了它。适用于gnome-term和urxvt AFAIK。


2
@masukomi如果您知道如何使用brew,则可以安装更新的bash并使用它。当使用升级的bash时,以上在我的mac终端上可以正常工作。
mcheema 2014年

是的,使用较新版本的bash很好。Hower提示字符串,例如$ PS1不使用回声转义格式
cms

6

您可能需要将代码点编码为八进制,以便迅速扩展以正确解码它。

编码为UTF-8的U + 2620是E2 98 A0。

所以在Bash中

export PS1="\342\230\240"

将使您的贝壳迅速进入头骨和骨骼。


嗨,我应该为“ e0 b6 85”输入什么代码?我如何找到它?
Udayantha Udy Warnasuriya

只需将十六进制(以16为底)的数字e0 b6 85转换为八进制(以8为底)-使用计算器可能是最简单的方法
cms 2013年

e0 b6 85十六进制为340266205八进制
cms 2013年

这很奏效,非常感谢!顺便说一句,您可以在这些页面上找到八进制版本:graphemica.com/%E2%9B%B5
Perlnika 2013年

6

在bash中打印Unicode字符以输出,请使用\ x,\ u或\ U(第一个代表2位十六进制,第二个代表4位十六进制,第三个代表任意长度)

echo -e '\U1f602'

我想使用$'...'语法将其分配给变量

x=$'\U1f602'
echo $x

5

如果您不介意Perl一线:

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

-CS对输入启用UTF-8解码,对输出启用UTF-8编码。-E使用say启用的现代功能,将下一个参数评估为Perl 。如果您不希望在末尾使用换行符,请使用print代替say


5

这三个命令中的任何一个都会在控制台中打印您想要的字符,但前提是该控制台可以接受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的)文本编辑器中。

如果需要查看如何以UTF-8编码此类Unicode代码点,请使用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。即,空格(十六进制20)和换行(十六进制0A)之间的值。

如果您想深入研究将数字转换为字符:请看这里,以查看Greg Wiki(BashFAQ)中有关Bash中ASCII编码的文章!


回复:“或者,在HEX中避免错误...”我几乎认为将Unicode字符转换为您用十六进制字符表示的二进制编码不会帮助避免错误。在“ bash”中使用unicode表示法会更好地避免出现以下错误:“ \ uHHHH --- Unicode(ISO / IEC 10646)字符,其值是----十六进制值HHHH(一到四个十六进制数字); \ UHHHHHHHH ---- Unicode的(ISO / IEC 10646)字符,它的值是十六进制----值HHHHHHHH(一至八个十六进制数字)
阿斯塔拉

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

4

抱歉,这个老问题复活了。但是,当使用时,bash有一种非常简单的方法可以从纯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(在我的机器上花费不到2s。额外的空间是为了防止某些字符由于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到一个变量中(这极大地加快了工作速度)
  • local variable 有没有污染全局名称空间
  • local -n var=other别名varother,这样分配var会改变other。这里一个有趣的部分是var本地名称空间的other一部分,而全局名称空间的一部分。
    • 请注意,中没有localglobal名称空间之类的东西bash。变量保存在环境中,并且始终是全局变量。Local仅保留当前值并在再次离开该功能时将其恢复。从函数内部调用的其他函数local仍将看到“本地”值。这与其他语言中的所有常规范围界定规则从根本上来说是一个不同的概念(这样bash做非常强大,但是如果您是一个不了解它的程序员,则可能导致错误)。

好-对我根本不起作用。使用任何功能的任何尝试都会发出:第6行:本地:-n:无效选项本地:用法:本地名称[=值] ...我正在使用最新的(10.14.2)MacOS和bash(GNU bash ,版本3.2.57(1)-发行版(x86_64-apple-darwin18))
Motti Shneor,

4

以下是所有可用的Unicode表情符号的列表:

https://zh.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"
🌄

在OSX上回显\ U <hex>字符串不起作用,它仅输出引号中的内容。
masukomi


2

只需使用Python2 / 3单线即可:

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

结果是:


2

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

0

如果知道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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.