是否可以告诉 awk 计算 '%10s' printf 格式的字符串长度而不是字节字符串长度?

是否可以告诉 awk 计算 '%10s' printf 格式的字符串长度而不是字节字符串长度?

尝试以下输出|Ü| 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 中字符长度的更多信息。

相关内容