![为什么我使用此脚本收到错误“print_unicode:第 9 行:printf:缺少 \u Unicode 0187 的 unicode 数字”](https://linux22.com/image/86568/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E4%BD%BF%E7%94%A8%E6%AD%A4%E8%84%9A%E6%9C%AC%E6%94%B6%E5%88%B0%E9%94%99%E8%AF%AF%E2%80%9Cprint_unicode%EF%BC%9A%E7%AC%AC%209%20%E8%A1%8C%EF%BC%9Aprintf%EF%BC%9A%E7%BC%BA%E5%B0%91%20%5Cu%20Unicode%200187%20%E7%9A%84%20unicode%20%E6%95%B0%E5%AD%97%E2%80%9D.png)
我按照 shell 脚本编写了它们,以查看 Unicode 字符在我的终端上的样子。
#!/bin/bash
X=0
while [ $X -lt 65536 ]; do
HEX=`bc <<< "obase=16; $X"`
HEX="0x${HEX}"
UCODENAME=`printf "%0*x\n" 4 $HEX`
UCODECHAR=`printf "\u%0*x\n" 4 $HEX`
echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
X=$((X + 1))
done
当我运行脚本时,我收到以下输出:
print_unicode: line 9: printf: missing unicode digit for \u
Unicode 0188 = ƈ
第二行正是我正在寻找的。
我确实尝试严格使用printf
以消除错误。
#!/bin/bash
X=0
while [ $X -lt 65536 ]; do
HEX=`bc <<< "obase=16; $X"`
HEX="0x${HEX}"
printf 'Unicode %0*x = \u%0*x\n' 4 $HEX 4 $HEX
X=$((X + 1))
done
我得到以下输出:
print_unicode: line 8: printf: missing unicode digit for \u
Unicode 037f = \u037f
第二行不是我要找的,我仍然收到相同的错误消息。
我该如何修复这个错误?
奖励:对此有什么更优雅的解决方案?
答案1
你得到的错误的原因是:
内置 printf仅当它后面跟着一个实际数字时才理解\U
(或):\u
$ printf '\U0021'
!
为了使其创建数字并转换它,需要一个两步 printf (需要一个双 \ 来传递双引号):
$ printf '%b' "$(printf '\\U%04X' 33)"
!
如你所愿:
$ printf '%b' "$(printf '\\u%0*X' 4 33)"
!
这也有效:
$ printf '%b' "$(printf '\\U%0*X' 8 33)"
!
不需要使用 bc 告诉 bash 十六进制数字。
bash 可以很好地理解这一点:
$ a=$(( 0xdef )); echo $(( a + 1 ))
3568
获取数字的十六进制值printf
就足够了:
$ printf '0x%06x' 3568
0x000df0
该循环可以简化为:
#!/bin/bash
cp=$((0x020)) len=6
for (( cp=32; cp<$((0x010000)); cp++)); do
Ucode="$(printf '%b' "$(printf '\\U%0*X' "$len" "$cp")")"
printf 'Unicode U%0*x = %s\n' 4 "$cp" "$Ucode"
done
谨防从 0x20 到 0x010000 有很多行(~ 64k 行)。
我将 len 增加到 6,因为 UNICODE 的代码点最多可达 10FFFF。
当然,Ucode 的完整定义是这样的:
Ucode="$(printf '%b' "$(printf '\\U%0*X' $len "$cp")")"
cp
请注意, dec=32 或 HEX=0x20 以下的代码点 ( ) 是控制字符。
即使代码适用于这样的代码点我也这样做不是推荐你和他们一起玩。
除了对于 UNICODE U0000,因为该值被分配给变量。
这打印\0
$ printf '%b' "$(printf '\\U%0*X' "6" "0")"
与 xxd 确认:
$ printf '%b' "$(printf '\\U%0*X' "6" "0")" | xxd
0000000: 00
警告:4.3 以下的 Bash 无法正确编码 utf-8 中 U0080 和 U00FF 之间的值。请使用 4.3 或 4.4 版本。
答案2
我不断尝试找到了解决方案。
#!/bin/bash
X=0
while [ $X -lt 65536 ]; do
HEX=`bc <<< "obase=16; $X"`
HEX="0x${HEX}"
UCODE=`printf "%0*x\n" 4 $HEX`
printf "Unicode ${UCODE} = \u${UCODE}\n"
X=$((X + 1))
done
我想到以这种方式尝试 printf: https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
我仍然愿意看到更优雅的解决方案。
答案3
您可以用不同的方式执行此操作(因为 bash 似乎忽略u
in周围的转义反斜杠"\u"
):
#!/bin/bash
X=0
while [ $X -lt 65536 ]; do
HEX=$(bc <<< "obase=16; $X")
HEX="0x${HEX}"
UCODENAME=$(printf "%0*x\n" 4 $HEX)
UCODECHAR="\\u$(printf "%0*x" 4 $HEX)"
echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
X=$((X + 1))
done
当然,该脚本仍然是特定于 bash 的。其他一些评论:
- 大多数人会建议使用
$(
and)
而不是 back-tics。 - bash
printf
可以直接打印 Unicode(不需要 echo)。 - 额外的
printf
forUCODECHAR
是多余的
消除冗余:
#!/bin/bash
X=0
while [ $X -lt 65536 ]; do
HEX=$(bc <<< "obase=16; $X")
HEX="0x${HEX}"
UCODENAME=$(printf "%0*x\n" 4 $HEX)
UCODECHAR="\\u${UCODENAME}"
echo -e "Unicode ${UCODENAME} = ${UCODECHAR}"
X=$((X + 1))
done