【问题标题】:How do you echo a 4-digit Unicode character in Bash?你如何在 Bash 中回显一个 4 位 Unicode 字符?
【发布时间】:2010-10-10 19:34:19
【问题描述】:

我想将 Unicode 骷髅和交叉骨添加到我的 shell 提示符中(特别是 'SKULL AND CROSSBONES' (U+2620)),但我不知道魔法咒语使 echo 吐出它或任何其他 4 位 Unicode 字符。两位数的很容易。例如,echo -e "\x55",.

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

在 macOS 的终端应用程序中,转到 Preferences-> Encodings 并选择 Unicode (UTF-8)。

【问题讨论】:

  • 请注意,您的 "2 digit one's are easy (to echo)" 注释仅对 UTF-8 语言环境中不超过 "\x7F" 的值有效(@ 987654323@ 标签表明您的标签是)... 单字节 表示的模式永远不会在\x80-\xFF 范围内。此范围在单字节 UTF-8 字符中是非法的。例如,U+0080(即\x80)的 Unicode 代码点值实际上是 UTF-8 中的 2 个字节。\xC2\x80..
  • 例如printf "\\u007C\\u001C".
  • NB: 对我来说,gnome-terminalecho -e '\ufc' 不会产生 ü,即使字符编码设置为 UTF-8。但是,例如 urxvt 确实会按预期打印例如 printf "\\ub07C\\ub01C"(不带有 � 或框)。
  • @Peter.O 为什么bash 标签是一个有用的提示?不同的终端在 CJK 或......中是常见的吗?
  • @Peter.O zsh、fish、scsh、elvish 等...有许多不同的 shell,每个都可以处理他们想要(或不想要)的 unicode 字符。 “bash”清楚地表明这个问题不是关于一些奇怪的外壳,它做事不同。

标签: bash shell unicode character-encoding


【解决方案1】:

只需在您的 shell 脚本中输入“☠”即可。在正确的语言环境和支持 Unicode 的控制台上,它会打印得很好:

$ echo ☠
☠
$

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

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

【讨论】:

    【解决方案2】:

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

    $ printf '\xE2\x98\xA0'
    ☠
    

    要检查控制台如何对其进行编码,请使用 hexdump:

    $ printf ☠ | hexdump
    0000000 98e2 00a0                              
    0000003
    

    【讨论】:

    • 我的输出是“���”而不是☠...这是为什么?
    • 确实如此。我发现我使用的是LANG=C 而不是LANG=en_US.UTF-8。现在我在 Gnome 中的终端正确地显示了符号……但真正的终端(tty1-6)仍然没有。
    • 对于那些尝试 hexdump 的人:0000000 f0 9f 8d ba 转换为 \xf0\x9f\x8d\xba。示例回显:echo -e "\xf0\x9f\x8d\xba".
    • 您还可以使用$'...' 语法将编码字符放入变量中,而无需使用$(...) 捕获子shell,用于本身不解释转义序列的上下文:@ 987654330@
    • 关于 hexdump 的另一件事:在我的机器上,答案中的第二个命令输出 0000000 98e2 00a0。当然0000000只是一个不重要的偏移量,但是它后面的字节转换为\xe2\x98\xa0,因为机器使用的是小端字节序。
    【解决方案3】:
    % echo -e '\u2620'     # \u takes four hexadecimal digits
    ☠
    % echo -e '\U0001f602' # \U takes eight hexadecimal digits
    ?
    

    这适用于 Zsh(我检查了 4.3 版)和 Bash 4.2 或更高版本。

    【讨论】:

    • 当我这样做时会吐出\u2620。
    • 对不起,忘了说我用的是zsh。
    • 在 Bash 4.2 中添加了对 \u 的支持。
    • 有一个使用 ANSI 字符串的版本echo $'\U1f602'
    • 不适用于我,Mac OS 10.14.2,bash(GNU bash,版本 3.2.57(1)-release (x86_64-apple-darwin18))。它只是打印出输入 - $ echo -e '\u2620' 只是打印出: \u2620
    【解决方案4】:

    快速将 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'   # ☠
    

    【讨论】:

    • 我不会调用上面的示例 quick(有 11 个命令及其参数)...而且它只处理 3 字节 UTF-8 字符`(UTF-8 chars 可以是 1、2 或 3 个字节)...这有点短,适用于 1-3++++ 字节:printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u) .... xxd 作为一部分提供'vim-common' 包
    • PS:我刚刚注意到上面的 hexdump/awk 示例正在交换字节对中的字节序列。这适用于 UTF-8 转储。如果它是 UTF-16LE 的转储并且想要输出 Unicode Codepoints,那将是相关的,但在这里没有意义,因为输入是 UTF-8 并且输出与输入完全相同(加上每个 hexdigit-pair 之前的 \x)
    • UTF-8 字符可以是 1 - 4 个字节的序列
    • 基于@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
    • 好人。考虑一下:codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ ? z ...享受?
    【解决方案5】:

    只要您的文本编辑器能够处理 Unicode(可能以 UTF-8 编码),您就可以直接输入 Unicode 代码点。

    例如,在 Vim 文本编辑器中,您将进入插入模式并按 Ctrl + V + U 然后代码点编号为 4 位十六进制数(必要时用零填充)。所以你会输入 Ctrl + V + U 2 6 2 0。见:What is the easiest way to insert Unicode characters into a document?

    在运行 Bash 的终端上,您可以输入 CTRL+SHIFT+U 并输入所需字符的十六进制代码点.在输入过程中,您的光标应显示带下划线的u。您键入的第一个非数字结束输入,并呈现字符。因此,您可以使用以下命令在 Bash 中打印 U+2620:

    echo CTRL +SHIFT+U2620 回车回车

    (第一个输入结束 Unicode 输入,第二个运行echo 命令。)

    信用:Ask Ubuntu SE

    【讨论】:

    • 十六进制代码点的良好来源是unicodelookup.com/#0x2620/1
    • 当 ctrl-v 和 u 之间有一个点时,我使用的 vim 版本(RHEL 6.3 上的 7.2.411)没有响应,但是当那个点是省略。
    • @ChrisJohnson:我已经从说明中删除了句点,它不是用来按键的(这就是它没有出现键盘效果的原因)。很抱歉造成混乱。
    • 当心:只有当你在 GTK+ 环境下(如 Gnome)运行 Bash 时,它才能在运行 Bash 的终端中工作。
    • C-S-u 2 6 2 0 的功能是您的终端仿真器、X 输入法 (XIM) 或类似的功能。 AFAIK,您将无法将SHIFTCTRL 发送到终端层。终端只用字符说话,而不是像 X 服务器那样的键符和键码(同样,它是 7 位的,用于所有意图和目的)。在这个世界上,CTRL 掩盖了 4 个最高有效位(& 0b00001111),从而导致
    【解决方案6】:

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

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

    所以在 Bash 中,

    export PS1="\342\230\240"
    

    会让你的 shell 提示成头骨和骨头。

    【讨论】:

    • 嗨,我应该为“e0 b6 85”输入什么代码?我怎样才能找到它?
    • 只需将十六进制(以 16 为底)数字 e0 b6 85 转换为八进制(以 8 为底) - 使用计算器可能是最简单的方法
    • e0 b6 85 十六进制是 340 266 205 八进制
    • 成功了,非常感谢!顺便说一句,您可以在以下页面找到八进制版本:graphemica.com/%E2%9B%B5
    【解决方案7】:

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

    输出是:

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

    【讨论】:

    • 我很好奇round-about方法背后的推理,以及REPLY变量的具体使用方法。我假设您检查了 bash 源代码或运行过或需要优化的东西,我可以看到您的选择可以如何优化,尽管高度依赖于解释器)。
    【解决方案8】:

    我正在使用这个:

    $ echo -e '\u2620'
    ☠
    

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

    【讨论】:

    • @masukomi 如果您知道如何使用 brew,您可以安装更新的 bash 并使用它。使用升级后的 bash 时,上述内容在我的 mac 终端上运行良好。
    • 是的,新版本的 bash 没问题。 Hower 提示字符串,例如 $PS1 不使用回显转义格式
    【解决方案9】:

    基于 Stack Overflow 问题Unix cut, remove first tokenhttps://stackoverflow.com/a/15903654/781312

    (octal=$(echo -n ☠ | od -t o1 | head -1 | cut -d' ' -f2- | sed -e 's#\([0-9]\+\) *#\\0\1#g')
    echo Octal representation is following $octal
    echo -e "$octal")
    

    输出如下。

    Octal representation is following \0342\0230\0240
    ☠
    

    【讨论】:

      【解决方案10】:

      这三个命令中的任何一个都将在控制台中打印您想要的字符,前提是控制台确实接受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
      

      或者,在 HEX 中避免错误:0xE2 0x98 0xA0。即空格(HEX 20)和换行符(Hex 0A)之间的值。

      如果您想深入了解将数字转换为字符:look here 查看 Greg 的 wiki (BashFAQ) 中有关 Bash 中 ASCII 编码的文章!

      【讨论】:

      • re:"或者,在 HEX 中避免错误..." 我几乎不认为将 unicode 字符转换为您用十六进制字符表示的某种二进制编码,帮助避免错误。在“bash”中使用 unicode 表示法可以更好地避免错误,即:“ \uHHHH---值为----十六进制值 HHHH(一到四个十六进制数字)的 Unicode (ISO/IEC 10646) 字符;\UHHHHHHHH ----Unicode (ISO/IEC 10646) 字符,其值为----十六进制值 HHHHHHHH(1 到 8 个十六进制数字)
      【解决方案11】:

      printf 内置函数(就像 coreutils 的 printf)知道接受 4 位 Unicode 字符的 \u 转义序列:

         \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
      【解决方案12】:

      如果您不介意 Perl 单行:

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

      -CS 在输入上启用 UTF-8 解码,在输出上启用 UTF-8 编码。 -E 将下一个参数评估为 Perl,并启用了诸如 say 之类的现代功能。如果您不想在末尾换行,请使用print 而不是say

      【讨论】:

        【解决方案13】:

        如果 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"
        

        【讨论】:

          【解决方案14】:

          使用 Python2/3 单行代码很容易:

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

          结果:

          【讨论】:

            【解决方案15】:

            很抱歉再次提出这个老问题。但是当使用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 代码点转储到标准输出(在我的机器上花费不到 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 的输出收集到一个变量中,无需分叉(这极大地加快了速度)
            • local variable 是为了不污染全局命名空间
            • local -n var=othervar 别名为 other,这样分配给 var 就会改变 other。这里一个有趣的部分是,var 是本地命名空间的一部分,而 other 是全局命名空间的一部分。
              • 请注意bash 中没有localglobal 命名空间。变量保存在环境中,并且始终是全局的。 Local 只是将当前值收起,并在再次离开该函数时将其恢复。使用local 从函数内部调用的其他函数仍将看到“本地”值。这是一个与其他语言中的所有常规范围规则完全不同的概念(bash 的功能非常强大,但如果您是不了解这一点的程序员,可能会导致错误)。

            【讨论】:

            • 好吧——对我来说根本不起作用。任何尝试使用您的任何功能,都会发出:第 6 行:本地:-n:无效选项本地:用法:本地名称 [= 值] ...我正在使用最新的 (10.14.2) MacOS 和 bash (GNU bash , 版本 3.2.57(1)-release (x86_64-apple-darwin18))
            【解决方案16】:

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

            echo -e '\U1f602'
            

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

            x=$'\U1f602'
            echo $x
            

            【讨论】:

              【解决方案17】:

              这里列出了所有可用的 unicode 表情符号:

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

              例子:

              echo -e "\U1F304"
              ?
              

              使用 hexdump 获取该字符的 ASCII 值

              echo -e "?" | hexdump -C
              
              00000000  f0 9f 8c 84 0a                                    |.....|
              00000005
              

              然后使用十六进制格式通知的值

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

              【讨论】:

              • 回显 \U 字符串在 OSX 上不起作用,它只会准确输出引号中的内容。
              【解决方案18】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-12-11
                • 2021-01-27
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-02-26
                相关资源
                最近更新 更多