8

有几种特定于 shell 的方法可以在字符串中包含“unicode 文字”。例如,在 Bash 中,带引号的字符串扩展机制$''允许我们直接嵌入一个不可见字符:$'\u2620'.

但是,如果您尝试编写通用的跨平台 shell 脚本(通常,这可以被截断为“在 Bash、Zsh 和 Dash 中运行”),这不是一个可移植的特性。

我可以使用如下构造可移植地实现 ASCII 表(八进制数空间)中的任何内容:

WHAT_A_CHARACTER="$(printf '\036')"

…但是,POSIX / Dashprintf仅支持八进制转义。

显然,我还可以通过将任务转移到更完整的编程环境来实现完整的 Unicode 空间:

OH_CAPTAIN_MY_CAPTAIN="$(ruby -e 'print "\u2388"')"
TAKE_ME_OUT_TONIGHT="$(node -e 'console.log("\u266C")')"

那么:将这样的字符编码为shell脚本的最佳方法是什么,即:

  1. 适用于dash,bashzsh,
  2. 显示代码中代码点的十六进制编码,
  3. 不依赖于字符串的特定编码(即不通过以八进制编码 UTF-8 字节)
  4. 最后,不需要调用任何“重”解释器。(比方说,少于 0.01 秒的运行时间。)
4

2 回答 2

11

如果您printf安装了 Gnu(例如,它在 debian packagecoreutils中),那么您可以通过避免使用 shell 的内置命令来独立于您使用的 shell:

env printf '\u2388\n'

在这里,我使用 Posix 标准env命令来避免使用printf内置命令,但如果您碰巧知道在哪里printf,您可以直接使用完整路径来执行此操作,例如

/usr/bin/printf '\u2388\n'

如果你的外部printf和你的 shell 的内置printf都只实现 Posix 标准,你需要更加努力。一种可能性是用于iconv转换为 UTF-8,但是虽然 Posix 标准要求有一个命令iconv,但它并没有以任何方式规定标准编码的命名方式。我认为以下内容将适用于大多数与 Posix 兼容的平台,但创建的子 shell 的数量可能足以使其效率低于“重型”脚本解释器:

printf $(printf '\\%o' $(printf %08x 0x2388 | sed 's/../0x& /g')) |
iconv -f UTF-32BE -t UTF-8

上面使用printf内置函数将十六进制代码点值强制为 8 个十六进制数字长,然后sed将它们重写为 4 个十六进制常量,然后printf再次将十六进制常量更改为八进制表示法,最后printf将八进制字符常量解释为四字节序列,可以iconv作为大端 UTF-32 输入。(使用printf识别\x转义码的 a 会更简单,但 Posix 不需要,dash也没有实现它。)

您可以不加修改地使用该行打印多个符号,只要您为所有符号提供 Unicode 代码点(作为整数常量)(在 中执行的示例dash):

$ printf $(printf '\\%o' $(printf %08x 0x2388 0x266c 0xA |
>                          sed 's/../0x& /g')) |
> iconv -f UTF-32BE -t UTF-8
⎈♬
$

注意:正如 Geoff Nixon 在评论中提到的那样,鱼壳(它与 Posix 标准相去甚远,据我所知,它没有要求符合)将抱怨未引用的%08x格式参数printf,因为它需要以开头的单词%成为工作规范。因此,如果您使用 fish,请在格式参数中添加引号。

于 2014-12-26T15:37:41.053 回答
-3

我会去

echo -e "\xc3\xb6"

检查一下:

~ $ echo -e "\xc3\xb6"
ö
~ $ echo -n ö | hexdump
0000000 b6c3                                   
0000002
于 2014-12-26T07:12:56.733 回答