在POSIX shell脚本中嵌入Unicode字符的最佳方法是什么?

ELL*_*BLE 6 unicode bash shell posix dash-shell

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

但是,如果您尝试编写通用的跨平台shell脚本(通常,这可以被截断为"以Bash,Zsh和Dash运行."),这不是便携式功能.

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

WHAT_A_CHARACTER="$(printf '\036')"
Run Code Online (Sandbox Code Playgroud)

...但是,POSIX/Dash printf仅支持八进制转义.

通过将任务转化为更全面的编程环境,我显然也可以实现完整的Unicode空间:

OH_CAPTAIN_MY_CAPTAIN="$(ruby -e 'print "\u2388"')"
TAKE_ME_OUT_TONIGHT="$(node -e 'console.log("\u266C")')"
Run Code Online (Sandbox Code Playgroud)

那么:将这样的字符编码成shell脚本的最佳方法是:

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

ric*_*ici 8

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

env printf '\u2388\n'
Run Code Online (Sandbox Code Playgroud)

这里我使用Posix-standard env命令来避免使用printf内置printf函数,但如果您碰巧知道在哪里可以直接使用完整的路径,例如

/usr/bin/printf '\u2388\n'
Run Code Online (Sandbox Code Playgroud)

如果你的外部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
Run Code Online (Sandbox Code Playgroud)

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

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

$ printf $(printf '\\%o' $(printf %08x 0x2388 0x266c 0xA |
>                          sed 's/../0x& /g')) |
> iconv -f UTF-32BE -t UTF-8
??
$
Run Code Online (Sandbox Code Playgroud)

注意:正如Geoff Nixon在评论中提到的那样,鱼壳(它无法接近Posix标准,并且据我所知,没有愿意遵守)会抱怨不带引号的%08x格式参数printf,因为它期望单词以%成为职业生涯.因此,如果您使用fish,请在format参数中添加引号.

  • [`command`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html)命令是为了您使用`env`的目的而设计的. (2认同)