Here are a couple of approaches:
With Rounding
As you appear to realize, printf
is a great tool for formatted printing.
%f
is the format to output a “floating point number”,
i.e., a number that is not an integer,
i.e., a number that includes a fractional part.
As with all printf formats,
a number (an integer!) immediately after the %
specifies the overall length of the formatted output,
possibly including spaces.
This number can be followed by a period (.
) and another number,
which specifies the number of decimal positions
(digits to the right of the decimal point) to display.
So, for example, the commands
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
︙
will produce the output
1000000.00
1000.00
1.00
1.23
1.68
︙
Note that 1.6789
got rounded up to 1.68
.
So you should be able to get the result you want with this command:
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
The space=$(…)
command is just the port space
command substitution
that we’ve been using all along, but with the result
(which looks like 0.088 MiB
, for example)
assigned to a temporary variable, space
.
Then space_num=${space% *}
sets space_num
to the part of that
before the space character (i.e., the number; 0.088
in this example),
and space_mib=${space#* }
sets space_mib
to the part
after the space character (i.e., the units, MiB
).
Finally we glue all the parts together,
using printf
%12.2f
to display the number to two decimal places,
rounding to the nearest hundredth,
and lining up on the decimal points (as in the earlier example).
For your data, this looks like
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
Note that 0.088 MiB
got rounded up to 0.09 MiB
Note also that space_mib
is always set to MiB
,
so we don’t actually need to compute it;
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
does the same as the above.
With Truncation
To simply truncate numbers,
it’s best to treat them as strings rather than as numbers.
This command
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/'
starts off as pretty much the same as my previous answer.
But then it pipes everything through a sed
, which has two parts:
s/(\..*) /\100 /
This matches the decimal point (\.
)
and any number of characters after it (.*
)
up to, but not including, a space character.
Then it replaces the whole match string
with the part before the space (\1
), two zeroes, and a space.
(I could have said s/(\..*)( )/\100\2/
; it would done the same thing.)
This changes 0.088 MiB
to 0.08800 MiB
and 0.0 MiB
to 0.000 MiB
.
If you had 42. MiB
in your data, it would change it to 42.00 MiB
.
But note that it assumes that every number has a decimal point,
even if it doesn’t have any digits after it.
(It also assumes that there are no periods
in the strings mtr
, pkgconfig
, libiconv
, etc.)
We need to do this to ensure
that there are at least two digits after the decimal point in every number;
that is not the case for gperf
until we make this correction.
s/(.{25}) *(....\...).*( .*)/\1\2\3/
.{25}
is short for .........................
;
i.e., 25 arbitrary characters.
I assume this is long enough to capture the longest (e.g., libiconv
),
most deeply indented, string.
Then any number of characters (.*
),
which, in fact, I expect to be just a bunch of spaces.
Then ....\...
matches four characters, a decimal point,
and two more characters.
If you ever have a space number bigger than 9999
,
you’ll have to change this
to match more than four digits to the left of the decimal point.
Then any number of characters (.*
),
which will be any digits after the first two after the decimal point.
Then a space and the rest of the line (( .*)
),
which I expect to be MiB
.
Then it puts the pieces back together as the string
(with the appropriate leading and following spaces),
the number (with enough leading spaces
to make the .
the fifth character in \2
), and then the units.
The output from this command looks like
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
Note that 0.088 MiB
got truncated to 0.08 MiB
.
Of course you can put any of the above compound commands all on one line
if you want, and you should adjust the width constants
(12
, 20
, 25
, 30
, etc.) to meet your needs.
while
loop, if you refer to this) is what you see on your left — each line is echoed plus four tabs and the output of a command that's run on this same line. On the left you have a hierarchy of dependencies, that I want to preserve — on the right you have the size value for each one separated by four tabs. Instead of four tabs, I'd like to have those numbers aligned by its floating point, say at column 40 or so. You can see this inpkgconfig
and the 2 following lines. – 1.61803 Mar 27 '16 at 11:21