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

Har*_*ald 2 character-encoding awk unicode

尝试以下输出|\xc3\x9c| X|

\n
echo \'\xc3\x9c X\' | awk \'{printf("|% 2s|% 2s|\\n", $1, $2)}\'\n
Run Code Online (Sandbox Code Playgroud)\n

显然awk计算的是字节长度,而不是 的字符长度\xc3\x9c,因此计数为 2,并且不需要用空格进行左填充,就像 一样X

\n

是否可以awk在计算模式字符长度%<count>s printf而不是字节长度的模式下运行?

\n

\'s也存在同样的bashprintf问题。我希望答案不一样:“passthrough to libc printf”:-/

\n

没有使用,但是Ubuntu\xc2\xa022.04 (Jammy Jellyfish) 已经为我安装了gawk任何版本。我没想到这些天可以安装任何东西:-/gawk

\n

Ed *_*ton 5

GNU awk(可能还有其他一些 awk 变体):

\n
$ echo \'\xc3\x9c X\' | LC_ALL=\'en_US.UTF-8\' awk \'{printf "|% 2s|% 2s|\\n", $1, $2}\'\n| \xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

Bash 3.0+(可能还有一些其他 shell,可能进行了调整):

\n
$ LC_ALL=\'en_US.UTF-8\' a=\'\xc3\x9c\' b=\'X\'\n$ printf \'|%*s%s|%*s%s|\\n\' "$(( 2 - ${#a} ))" \'\' "$a" "$(( 2 - ${#b} ))" \'\' "$b"\n| \xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,bash 版本必须LC_ALL在正在执行的 shell 中设置${#a},而不仅仅是在版本printf发生的环境中设置awk,因此如果您不想LC_ALL在调用 shell 中更改,则需要保存/恢复它, ie o="$LC_ALL"; LC_ALL=\'en_US.UTF-8\' ... "$b"; LC_ALL="$o",或者在子 shell 中执行所有操作, ie ( LC_ALL=\'en_US.UTF-8\' ... "$b" )

\n

说明:

\n

来自GNU awk 文档

\n
\n
-b\n--characters-as-bytes\n
Run Code Online (Sandbox Code Playgroud)\n

使 gawk 将所有输入数据视为单字节字符。此外,所有使用 print 或 printf 写入的输出都被视为单字节字符。

\n

通常,gawk 遵循 POSIX 标准并尝试根据当前语言环境处理\n输入数据(请参阅“你所在位置的差异”)。这通常涉及将多字节字符转换为宽字符(内部),并且如果输入数据不包含有效的多字节字符,则可能会导致问题或混乱。这个选项是告诉gawk的简单方法,\xe2\x80\x9c放弃我的\n数据!\xe2\x80\x9d

\n
\n

使用 GNU awk 5.2.2 设置适当的语言环境会将多字节字符视为单个多字节字符:

\n
$ echo \'\xc3\x9c X\' | LC_ALL=\'en_US.UTF-8\' awk \'{printf "|% 2s|% 2s|\\n", $1, $2}\'\n| \xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

而使用不同的语言环境或使用-b,会将所有输入视为单字节字符:

\n
$ echo \'\xc3\x9c X\' | LC_ALL=\'C\' awk \'{printf "|% 2s|% 2s|\\n", $1, $2}\'\n|\xc3\x9c| X|\n\n$ echo \'\xc3\x9c X\' | awk -b \'{printf "|% 2s|% 2s|\\n", $1, $2}\'\n|\xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

使用时,-b结果与您的区域设置无关:

\n
$ echo \'\xc3\x9c X\' | LC_ALL=\'en_US.UTF-8\' awk -b \'{printf "|% 2s|% 2s|\\n", $1, $2}\'\n|\xc3\x9c| X|\n\n$ echo \'\xc3\x9c X\' | LC_ALL=\'C\' awk -b \'{printf "|% 2s|% 2s|\\n", $1, $2}\'\n|\xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

正如@St\xc3\xa9phaneChazelas在评论中提到的,请参阅Why is printf "shrinking" umlaut? printf对于shell 中的相关行为,其中@L\xc3\xa9a Gris\ 的答案表明这将获得字符计数,因此格式化的输出在 bash 3.0 及更高版本中是正确的:

\n
$ a=\'\xc3\x9c\' b=\'X\' LC_ALL=\'en_US.UTF-8\' \n$ printf \'|%*s%s|%*s%s|\\n\' "$(( 2 - ${#a} ))" \'\' "$a" "$(( 2 - ${#b} ))" \'\' "$b"\n| \xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

该功能也受区域设置的影响:

\n
$ LC_ALL=\'C\'\n$ printf "|%*s%s|%*s%s|\\n" "$(( 2 - ${#a} ))" \'\' "$a" "$(( 2 - ${#b} ))" \'\' "$b"\n|\xc3\x9c| X|\n
Run Code Online (Sandbox Code Playgroud)\n

有关获取 bash 中字符长度的更多信息,另请参阅length -of-string-in-bash 。

\n