带舍入

带舍入

如何才能最好地保持左侧的层次结构并对齐右侧的大小值?
另外,同时我想将大小四舍五入到一位或两位数。
我尝试过printf但无法弄清楚如何保持这种格式。

$ port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*/\1/" | while IFS= read -r line; do echo -e "$line" \\t\\t\\t\\t $(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2); done
mtr                  0.088 MiB
  pkgconfig                  0.615 MiB
    libiconv                 6.270 MiB
      gperf                  0.0 MiB
  glib2                  46.092 MiB
    xz               1.679 MiB
      gettext                24.825 MiB
        expat                1.109 MiB
        ncurses                  15.171 MiB
    libxml2                  10.405 MiB
      zlib               0.721 MiB
    libffi               0.141 MiB
    pcre                 5.954 MiB
      bzip2                  0.647 MiB
      libedit                0.795 MiB

这应该看起来像这样

mtr                          0.08 MiB
  pkgconfig                  0.61 MiB
    libiconv                 6.27 MiB
      gperf                  0.00 MiB
  glib2                     46.09 MiB
    xz                       1.67 MiB
      gettext               24.82 MiB
        expat                1.10 MiB
        ncurses             15.17 MiB
    libxml2                 10.40 MiB
      zlib                   0.72 MiB
    libffi                   0.14 MiB
    pcre                     5.95 MiB
      bzip2                  0.64 MiB
      libedit                0.79 MiB

答案1

这里有几种方法:

带舍入

正如您似乎意识到的那样,printf它是格式化打印的绝佳工具。  %f是输出“浮点数”的格式,即非整数的数字,即包含小数部分的数字。与所有 printf 格式一样,紧随其后的数字(整数!)% 指定格式化输出的总长度,可能包括空格。该数字后面可以跟一个句点 (.) 和另一个数字,指定要显示的小数位数(小数点右侧的数字)。例如,命令

printf "%12.2f\n" 1000000
printf "%12.2f\n" 1000
printf "%12.2f\n" 1
printf "%12.2f\n" 1.2345
printf "%12.2f\n" 1.6789

将产生输出

  1000000.00
     1000.00
        1.00
        1.23
        1.68

请注意,1.6789得到了四舍五入1.68

所以你应该能够使用这个命令得到你想要的结果:

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*/\1/" |
     while IFS= read -r line
     do
         space="$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2)"
         space_num=${space% *}
         space_mib=${space#* }
         printf "%-20s%12.2f %s\n" "$line" "$space_num" "$space_mib"
     done

space=$(…)命令只是port space我们一直使用的命令替换,但结果(例如0.088 MiB,看起来像 )分配给临时变量space。然后space_num=${space% *}设置space_num为空格字符之前的部分(即数字;0.088在本例中),并space_mib=${space#* }设置space_mib为空格字符之后的部分(即单位,MiB)。最后,我们将所有部分粘合在一起,用于printf %12.2f将数字显示为小数点后两位,四舍五入到最接近的百分位,并在小数点上对齐(如前面的示例所示)。对于您的数据,这看起来像

mtr                         0.09 MiB
  pkgconfig                 0.62 MiB
    libiconv                6.27 MiB
      gperf                 0.00 MiB
  glib2                    46.09 MiB
    xz                      1.68 MiB
      gettext              24.83 MiB
        expat               1.11 MiB
        ncurses            15.17 MiB
    libxml2                10.40 MiB
      zlib                  0.72 MiB
    libffi                  0.14 MiB
    pcre                    5.95 MiB
      bzip2                 0.65 MiB
      libedit               0.80 MiB

请注意,0.088 MiB四舍五入为0.09 MiB

另请注意,它space_mib始终设置为MiB,因此我们实际上不需要计算它;

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*/\1/" |
     while IFS= read -r line
     do
         space_num="$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1)"
         printf "%-20s%12.2f %s\n" "$line" "$space_num" "MiB"
     done

与上面的操作相同。

带截断

要简单地截断数字,最好将它们视为字符串而不是数字。这个命令

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*/\1/" |
     while IFS= read -r line
     do
         printf "%-30s%s\n" "$line" \
                  "$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2)"
     done | sed -E -e 's/(\..*) /\100 /' -e 's/(.{25}) *(....\...).*( .*)/\1\2\3/'

开始与我之前的答案几乎相同。但随后它通过 a 管道传输所有内容sed,它有两个部分:

  • s/(\..*) /\100 /
    这与小数点 ( \.) 及其后的任意数量的字符 ( .*) 匹配,最多但不包括空格字符。然后,它将整个匹配字符串替换为空格 ( ) 之前的部分\1、两个零和一个空格。 (我本可以说s/(\..*)( )/\100\2/;它会做同样的事情。)这将更0.088 MiB改为0.08800 MiB和。如果您的数据中有,它会将其更改为.但请注意,它假设每个数字都有一个小数点,即使它后面没有任何数字。 (它还假设字符串、、等中没有句点)0.0 MiB0.000 MiB42. MiB42.00 MiBmtrpkgconfiglibiconv

    我们需要这样做以确保每个数字的小数点后至少有两位数;gperf在我们进行此更正之前,情况并非如此。

  • s/(.{25}) *(....\...).*( .*)/\1\2\3/
    .{25}.........................是;的缩写即 25 个任意字符。我认为这足够长,可以捕获最长(例如,libiconv)、缩进最深的字符串。然后是任意数量的字符 ( .*),事实上,我希望它只是一堆空格。然后....\...匹配四个字符、一个小数点和另外两个字符。如果您的空格数大于9999,则必须更改它以匹配小数点左侧四位以上的数字。然后是任意数量的字符 ( .*),即小数点后前两位之后的任意数字。然后是一个空格和该行的其余部分 ( ( .*)),我希望是 MiB。然后它将这些片段重新组合在一起,作为字符串(具有适当的前导和后继空格)、数字(具有足够的前导空格以使.)中的第五个字符\2,然后是单位。

该命令的输出如下所示

mtr                         0.08 MiB
  pkgconfig                 0.61 MiB
    libiconv                6.27 MiB
      gperf                 0.00 MiB
  glib2                    46.09 MiB
    xz                      1.67 MiB
      gettext              24.82 MiB
        expat               1.10 MiB
        ncurses            15.17 MiB
    libxml2                10.40 MiB
      zlib                  0.72 MiB
    libffi                  0.14 MiB
    pcre                    5.95 MiB
      bzip2                 0.64 MiB
      libedit               0.79 MiB

请注意,0.088 MiB被截断为0.08 MiB.

当然,如果您愿意,您可以将上述任何复合命令全部放在一行上,并且您应该调整宽度常量(12202530等)以满足您的需要。

答案2

正如您似乎意识到的那样,printf它是格式化打印的绝佳工具。您可能已经了解到,%s这是输出字符串的格式。  %20s输出一个 20 个字符的长字符串,右对齐,左侧填充空格(如有必要)。  %-20s则相反 - 它输出一个 20 个字符的长字符串,左对齐,右侧填充空格(如果需要)。例如,命令

printf "%-20s%s\n" "mtr" "0.088 MiB"
printf "%-20s%s\n" "  pkgconfig" "0.615 MiB"
printf "%-20s%s\n" "    libiconv" "6.270 MiB"

将产生输出

mtr                 0.088 MiB
  pkgconfig         0.615 MiB
    libiconv        6.270 MiB

所以你应该能够使用这个命令得到你想要的结果:

port rdeps mtr 2>/dev/null | sed -E "1 s/.*of (.*) @.*/\1/" |
     while IFS= read -r line
     do
         printf "%-20s%s\n" "$line" \
                  "$(port space --units MiB --total $line 2>/dev/null | cut -d ' ' -f 1-2)"
     done

我经常建议您应该始终$在 shell 命令和脚本中引用引用(例如变量),除非您有充分的理由不这样做。请注意,我将$(…)表达式放在引号中。但是,由于$line包含前导空格,因此很可能

port space --units MiB --total "$line"

行不通。

当然,如果您愿意,您可以将上述复合命令全部放在一行中,并且您应该根据20您的需要进行调整。

相关内容