![是否可以告诉 awk 计算 '%10s' printf 格式的字符串长度而不是字节字符串长度?](https://linux22.com/image/226346/%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%91%8A%E8%AF%89%20awk%20%E8%AE%A1%E7%AE%97%20'%2510s'%20printf%20%E6%A0%BC%E5%BC%8F%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%95%BF%E5%BA%A6%E8%80%8C%E4%B8%8D%E6%98%AF%E5%AD%97%E8%8A%82%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%95%BF%E5%BA%A6%EF%BC%9F.png)
尝试以下输出|Ü| X|
:
echo 'Ü X' | awk '{printf("|% 2s|% 2s|\n", $1, $2)}'
显然awk
计算的是字节长度,而不是 的字符长度Ü
,因此计数为 2,并且不需要用空格进行左填充,就像 一样X
。
是否可以awk
在一种重要的模式下运行特点模式的长度%<count>s
printf
,而不是字节长度?
这存在同样的bash
问题printf
。我希望答案不一样:“passthrough to libc printf”:-/
我曾是不是使用gawk
,但无论版本如何乌班图22.04(Jammy Jellyfish)已经为我安装了。我没想到gawk
这些天可以安装任何东西:-/
答案1
GNU awk(可能还有其他一些 awk 变体):
$ echo 'Ü X' | LC_ALL='en_US.UTF-8' awk '{printf "|% 2s|% 2s|\n", $1, $2}'
| Ü| X|
Bash 3.0+(可能还有一些其他 shell,可能需要进行调整):
$ LC_ALL='en_US.UTF-8' a='Ü' b='X'
$ printf '|%*s%s|%*s%s|\n' "$(( 2 - ${#a} ))" '' "$a" "$(( 2 - ${#b} ))" '' "$b"
| Ü| X|
请注意,bash 版本必须LC_ALL
在正在执行的 shell 中设置${#a}
,而不仅仅是在printf
版本发生的环境中设置awk
,因此如果您不想LC_ALL
在调用 shell 中更改,则需要保存/恢复它,即o="$LC_ALL"; LC_ALL='en_US.UTF-8' ... "$b"; LC_ALL="$o"
,或者在子 shell 中执行所有操作,即( LC_ALL='en_US.UTF-8' ... "$b" )
.
说明:
来自GNU awk 文档:
-b --characters-as-bytes
使 gawk 将所有输入数据视为单字节字符。此外,所有使用 print 或 printf 写入的输出都被视为单字节字符。
通常,gawk 遵循 POSIX 标准并尝试根据当前语言环境处理其输入数据(请参阅你所在的位置会有所不同)。这通常涉及将多字节字符转换为宽字符(内部),并且如果输入数据不包含有效的多字节字符,则可能会导致问题或混乱。这个选项是告诉 gawk“放开我的数据!”的简单方法。
使用 GNU awk 5.2.2 设置适当的语言环境会将多字节字符视为单个多字节字符:
$ echo 'Ü X' | LC_ALL='en_US.UTF-8' awk '{printf "|% 2s|% 2s|\n", $1, $2}'
| Ü| X|
而使用不同的语言环境或使用-b
,会将所有输入视为单字节字符:
$ echo 'Ü X' | LC_ALL='C' awk '{printf "|% 2s|% 2s|\n", $1, $2}'
|Ü| X|
$ echo 'Ü X' | awk -b '{printf "|% 2s|% 2s|\n", $1, $2}'
|Ü| X|
使用时,-b
结果与您的区域设置无关:
$ echo 'Ü X' | LC_ALL='en_US.UTF-8' awk -b '{printf "|% 2s|% 2s|\n", $1, $2}'
|Ü| X|
$ echo 'Ü X' | LC_ALL='C' awk -b '{printf "|% 2s|% 2s|\n", $1, $2}'
|Ü| X|
作为@StéphaneChazelas中提到的一条评论, 看为什么 printf 会“缩小”变音符号?printf
对于shell 中的相关行为,其中@Léa Gris 的回答建议这将获得字符计数,因此格式化的输出在 bash 3.0 及更高版本中是正确的:
$ a='Ü' b='X' LC_ALL='en_US.UTF-8'
$ printf '|%*s%s|%*s%s|\n' "$(( 2 - ${#a} ))" '' "$a" "$(( 2 - ${#b} ))" '' "$b"
| Ü| X|
该功能也受区域设置的影响:
$ LC_ALL='C'
$ printf "|%*s%s|%*s%s|\n" "$(( 2 - ${#a} ))" '' "$a" "$(( 2 - ${#b} ))" '' "$b"
|Ü| X|
也可以看看bash 中的字符串长度有关获取 bash 中字符长度的更多信息。