如何才能最好地保持左侧的层次结构并对齐右侧的大小值?
另外,同时我想将大小四舍五入到一位或两位数。
我尝试过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 MiB
0.000 MiB
42. MiB
42.00 MiB
mtr
pkgconfig
libiconv
我们需要这样做以确保是每个数字的小数点后至少有两位数;
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
.
当然,如果您愿意,您可以将上述任何复合命令全部放在一行上,并且您应该调整宽度常量(12
、20
、25
、30
等)以满足您的需要。
答案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
您的需要进行调整。