有几种特定于shell的方法可以在字符串中包含“unicode literal”。例如,在Bash中,引用的字符串扩展机制$''
允许我们直接嵌入一个不可见的字符:$'\u2620'
。
但是,如果您尝试编写通用的跨平台shell脚本(通常,这可以被截断为“以Bash,Zsh和Dash运行。”),那不是便携式的特征
我可以使用如下构造在ASCII表(八进制数字空间)中移植实现任何内容:
WHAT_A_CHARACTER="$(printf '\036')"
...但是,POSIX / Dash printf
仅支持八进制转义。
通过将任务转移到更完整的编程环境,我显然也可以实现完整的Unicode空间:
OH_CAPTAIN_MY_CAPTAIN="$(ruby -e 'print "\u2388"')"
TAKE_ME_OUT_TONIGHT="$(node -e 'console.log("\u266C")')"
那么:将这样的字符编码成shell脚本的最佳方法是:
dash
,bash
和zsh
,答案 0 :(得分:7)
如果您安装了Gnu printf
(例如,它位于debian包coreutils
中),那么您可以通过避免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
将八进制字符常量解释为四字节序列,可以作为大端UTF-32馈入iconv
。 (使用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
⎈♬
$
注意:正如Geoff Nixon在评论中提到的那样,鱼壳(它无法接近Posix标准,据我所知,没有任何愿望可以遵守)会抱怨未加引号%08x
的{{1}}格式参数,因为它希望以printf
开头的单词是jobspecs。因此,如果您使用fish,请在format参数中添加引号。
答案 1 :(得分:-3)
我会选择
echo -e "\xc3\xb6"
做检查:
~ $ echo -e "\xc3\xb6"
ö
~ $ echo -n ö | hexdump
0000000 b6c3
0000002