awk/printf 格式化数字奇怪的行为

jcd*_*ole 4 bash awk printf

下面的代码应该在第 1 列中留下填充编号。

\n
input="/home/user_install/folders_LIST_SIZES_MY_FOLDERS.txt"\nwhile IFS= read -r line ; do \n  sudo du  -sm $line | LC_NUMERIC=fr_FR.UTF8 awk '{printf "% 11 '\\''d  : %s\\n",  $1 , $2}'\ndone < "$input"\n\n
Run Code Online (Sandbox Code Playgroud)\n

预期结果应该是:

\n
    155\xe2\x80\xaf283  : /\n          0  : /100_samba\n        462  : /backup_sys\n         62  : /backup_sys_data\n          0  : /bdd\n          0  : /data\n          0  : /data1_pub\n          1  : /data3_dwnld_pub\n          0  : /data4_mk_dvd_pub\n          0  : /data5_my_tmp_pub\n      1\xe2\x80\xaf211  : /home\n          0  : /local\n         13  : /root\n          0  : /srv\n     33\xe2\x80\xaf313  : /virtual_0_backup_vdi\n     14\xe2\x80\xaf689  : /virtual_linux_1\n     99\xe2\x80\xaf116  : /virtual_win_1\n        300  : /win_linux_echange_1\n\n
Run Code Online (Sandbox Code Playgroud)\n

真正的结果是:

\n
  155\xe2\x80\xaf283  : /\n          0  : /100_samba\n        462  : /backup_sys\n         62  : /backup_sys_data\n          0  : /bdd\n          0  : /data\n          0  : /data1_pub\n          1  : /data3_dwnld_pub\n          0  : /data4_mk_dvd_pub\n          0  : /data5_my_tmp_pub\n    1\xe2\x80\xaf211  : /home\n          0  : /local\n         13  : /root\n          0  : /srv\n   33\xe2\x80\xaf313  : /virtual_0_backup_vdi\n   14\xe2\x80\xaf689  : /virtual_linux_1\n   99\xe2\x80\xaf116  : /virtual_win_1\n        300  : /win_linux_echange_1\n
Run Code Online (Sandbox Code Playgroud)\n

当数字超过 3 位时,左移 1 位。

\n

mar*_*rkp 6

使用输入数字1234567,123456123...

\n\n

对于第二组数据,就好像空格分隔符后面跟着一个退格键,或者也许......由多字节字符表示?

\n

将第二组数据通过管道传输到od -c我得到:

\n
0000000   1 302 240   2   3   4 302 240   5   6   7       :  \\n\n0000020       1   2   3 302 240   4   5   6       :  \\n\n0000040                   1   2   3       :  \\n\n0000052\n
Run Code Online (Sandbox Code Playgroud)\n

因此,该 1 字节空格分隔符实际上被实现为 2 个字节(302 240- 一个可打印字符,一个不可打印字符)。

\n

由于printf格式设置基于字节数而不是(可打印)字符数,因此每个不可打印字符“占用”一个输出位置,从而导致最终输出移动(或缩小)一个(可见/可打印)位置。

\n
\n

一种解决方法是将格式化分为两个单独的操作,例如:

\n
printf '1234567\\n123456\\n123\\n' |\nLC_NUMERIC=fr_FR.UTF8 awk '{ x = sprintf("%\\04711d",$1 )  # format just the number\n                             printf "%13s :\\n",x          # feed formatted number to basic string format\n                           }'\n
Run Code Online (Sandbox Code Playgroud)\n

这会生成:

\n
    1\xc2\xa0234\xc2\xa0567 :\n      123\xc2\xa0456 :\n          123 :\n
Run Code Online (Sandbox Code Playgroud)\n

注意: 2个字节仍然存在,即通过管道传输这组最新的数据来od -c生成

\n
0000000                   1 302 240   2   3   4 302 240   5   6   7\n0000020   :  \\n                           1   2   3 302 240   4   5   6\n0000040       :  \\n                                           1   2   3\n0000060       :  \\n\n0000063\n
Run Code Online (Sandbox Code Playgroud)\n
\n

一些注意事项:

\n
    \n
  • 如果 OP 期望处理 11 位数字,则(s)printf可能需要修改格式11d/13s

    \n
  • \n
  • 正如评论部分(KamilCuk、Ed Morton、我)中突出显示的那样,构成“空格分隔符”的实际字节甚至字节数(2 字节与 3 字节)可能会根据所用版本的不同而有所glibc不同建立fr_FR.UTF-8语言环境;对于 3 字节字符,这将导致每个分隔符将输出吃掉/移动 2 个(打印)字符;awk设计用于处理字符而不是字节,因此任何动态确定构成 1000 分隔符的字节数的尝试都需要额外的工作(例如:调用system(); 进行wc预计算bash,然后作为-v awk_var=byte_count参数传递给awk; awk -b

    \n
  • \n
\n