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

 mobiledu2502886443 发布于 2022-12-04 00:30

有几种特定于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,bashzsh,

    显示代码中代码点的十六进制编码,

    不依赖于字符串的特定编码(即不通过八进制编码UTF-8字节)

    最后,不需要调用任何"重型"解释器.(比方说,运行时间不到0.01秒.)

rici.. 8

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

env printf '\u2388\n'

这里我使用Posix-standard 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作为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
??
$

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

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

    env printf '\u2388\n'
    

    这里我使用Posix-standard 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作为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
    ??
    $
    

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

    2022-12-11 02:15 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有