2

I have the following multi-line output:

Volume Name: GATE02-SYSTEM
Size: 151934468096
Volume Name: GATE02-S
Size: 2000112582656

Is it possible to convert strings like:

Volume Name: GATE02-SYSTEM
Size: 141.5 Gb
Volume Name: GATE02-S
Size: 1862.75 Gb

with sed/awk? I looked for answers for the only number output like:

echo "151934468096" | awk '{ byte =$1 /1024/1024**2 ; print byte " GB" }'

but can't figure out how to apply it to my case.

EDIT: I'd like to clarify my case. I have the following one-liner:

esxcfg-info | grep -A 17 "\\==+Vm FileSystem" | grep -B 15 "[I]s Accessible.*true" | grep -B 4 -A 11 "Head Extent.*ATA.*$" | sed -r 's/[.]{2,}/: /g;s/^ {12}//g;s/\|----//g' | egrep -i "([V]olume Name|^[S]ize:)"

which generates output above. I want to know what to add more after the last command to get the desired output.

maaboo
  • 137
  • 2
  • 13

2 Answers2

3

Using (GNU) sed and bc:

... | sed 's@Size: \([0-9]*\)@printf "Size: %s" $(echo "scale = 2; \1 / 1024 ^ 3" | bc)GiB@e'
             ^^^^  ^^^^^^^^^^ ^^^^^^                    ^^^^^^^^^  ^^ ^^^^^^^^^^    ^^     ^
               1        2        3                          4       5      6         7     8
  1. Matching Size: lines.
  2. Capturing the number of bytes (potentially vulnerable if empty or zero, strengthen for production use)
  3. Replacing with printf output
  4. scale = sets the number of decimals for bc
  5. Using the sed capture group (number of bytes)
  6. The mathematical operation on the byte number
  7. Piping all that to bc for evaluation
  8. Telling sed to execute the "replace" part of the s statement in a subshell.

Another option is numfmt (where available, since GNU coreutils 8.21):

... | sed 's@Size: \([0-9]*\)@printf "%s" $(echo \1 | numfmt --to=iec-i --suffix=B)@e'

This doesn't give you control over the decimals, but "works" for all sizes (i.e. gives TiB where the number is big enough, and does not truncate to "0.00 GiB" for too-small numbers).

DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • Shoutout to @fedorqui for [this answer](https://stackoverflow.com/a/39350238/60281) on how to replace a `sed` capture group with the output of a shell command operating on that group. – DevSolar Nov 24 '17 at 13:38
1
printf "Volume Name: GATE02-SYSTEM\nSize: 151934468096\nVolume Name: GATE02-S\nSize: 2000112582656" |
awk '/^Size: / { $2 = $2/1024/1024**2 " Gb" }1'
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Hmm, possibly awk is somehow limited on ESXi. Adding `awk '/^Size: / { $2 = $2/1024/1024**2 " Gb" }'` generates zero output. – maaboo Nov 24 '17 at 13:33
  • Sorry, meant to edit in the final `1` but forgot. Updated now. – tripleee Nov 24 '17 at 14:38
  • If you end up piping to Awk anyway, your entire pipeline should probably be refactored to a single Awk script. Without access to the raw output from the tool, it's hard to say exactly how to do that. [The documentation](http://www.vi-toolkit.com/wiki/index.php/Esxcfg-info) hints at a scripting-friendly "Perl" output format, but doesn't show any example of that. Probably use that instead if you start refactoring this. – tripleee Nov 24 '17 at 14:45
  • I can reformat string after accepting it from the command on a remote host, using fully implemented awk and sed. Just wanted to do it as one-liner, but awk and sed are limited on ESXi. – maaboo Nov 24 '17 at 14:49
  • In what way is awk limited on ESXi? – Ed Morton Nov 24 '17 at 15:40