File size in human readable format
LinuxBashLinux Problem Overview
Given the size of a file in bytes, I want to format it with IEC (binary) prefixes to 3 significant figures with trailing zeros, e.g. 1883954 becomes 1.80M.
Floating-point arithmetic isn't supported in bash, so I used awk instead. The problem is I don't how to keep the trailing zeros. Current solution:
if [ $size -ge 1048576 ]
then
size=$(awk 'BEGIN {printf "%.3g",'$size'/1048576}')M
elif [ $size -ge 1024 ]
then
size=$(awk 'BEGIN {printf "%.3g",'$size'/1024}')K
fi
(The files aren't that big so I don't have to consider bigger units.)
Edit: There's another problem with this. See Adrian Frühwirth's comment below.
Linux Solutions
Solution 1 - Linux
Is there any reason you are not using
ls -lh
command ? If you are on a Linux system which has been released in the last few years, you have this functionality.
Solution 2 - Linux
GNU Coreutils contains an apparently rather unknown little tool called numfmt
for numeric conversion, that does what you need:
$ numfmt --to=iec-i --suffix=B --format="%.3f" 4953205820
4.614GiB
I think that suits your needs well, and isn’t as large or hackish as the other answers.
If you want a more powerful solution, look at my other answer.
Solution 3 - Linux
ls -lah /path/to/your/file | awk -F " " {'print $5'}
Solution 4 - Linux
Instead of using ls
and awk
to get the file size, use stat -c %s filename.ext
. It outputs only the number, with nothing else (at least on version 8.21). I can't use numfmt
because it's an older version which doesn't appear to use the printf syntax with decimal precision. I instead use the script below. I use the last line to test if the script is being sourced. If it's not, I can call it directly on the command line.
#!/bin/bash
function getFriendlyFileSize() {
OUT='/dev/null'
[ "$#" == 0 ] && echo 'No number given' && return 1
[ ! $(echo $1 | egrep -i '\-?[0-9]+') ] && echo 'Garbage data' && return 1
if [ "$1" == '' -o "$1" -lt 0 ] 2>$OUT
then
echo '0 B'
return 1
else
FSIZE=$1
fi
[ "$2" == '' ] && DECPTS=1 || DECPTS=$2
KB=1024
MB=1048576
GB=1073741824
TB=1099511627776
PB=1125899906842624
EB=1152921504606846976
LM=9223372036854775807 # bash comparison limit = 2^63-1 (signed int?)
[ "$FSIZE" -le 0 ] 2>$OUT && echo "0 B" && return
[ "$FSIZE" -lt $KB ] 2>$OUT && echo "$FSIZE B" && return
[ "$FSIZE" -lt $MB ] 2>$OUT && echo "$(echo "scale=$DECPTS;$FSIZE/$KB"|bc) KB" && return
[ "$FSIZE" -lt $GB ] 2>$OUT && echo "$(echo "scale=$DECPTS;$FSIZE/$MB"|bc) MB" && return
[ "$FSIZE" -lt $TB ] 2>$OUT && echo "$(echo "scale=$DECPTS;$FSIZE/$GB"|bc) GB" && return
[ "$FSIZE" -lt $PB ] 2>$OUT && echo "$(echo "scale=$DECPTS;$FSIZE/$TB"|bc) TB" && return
[ "$FSIZE" -lt $EB ] 2>$OUT && echo "$(echo "scale=$DECPTS;$FSIZE/$PB"|bc) PB" && return
[ "$FSIZE" -le $LM ] 2>$OUT && echo "$(echo "scale=$DECPTS;$FSIZE/$EB"|bc) EB" && return
[ "$?" -ne '0' ] 2>$OUT && echo "Bad input" && return 1
}
[[ $_ == $0 ]] && getFriendlyFileSize $1 $2
Solution 5 - Linux
I know that it's a little late. But may someone find it useful.
The answer is, simply, to use %.2f
instead of %.3g
in your script. (src)
Test:
#!/bin/bash
size=1883954
if [ $size -ge 1048576 ]
then
size=$(awk 'BEGIN {printf "%.2f",'$size'/1048576}')M
elif [ $size -ge 1024 ]
then
size=$(awk 'BEGIN {printf "%.2f",'$size'/1024}')K
fi
echo $size
The Output:
1.80M
Solution 6 - Linux
If you don't mind using bc
then the following will help do floating point operations. scale
can changed as per your needs depending on many digits you want to print.
size=1883954
if [ $size -ge 1048576 ]
then
size=$(echo "scale=2;$size/1048576"| bc)M
elif [ $size -ge 1024 ]
then
size=$(echo "scale=2;$size/1024" | bc)K
fi
echo $size
Solution 7 - Linux
If you happen to have Qalculate! installed (which is awesome by the way), there’s an easy trick:
human_readable="$( qalc -t set "precision $precision" "${in_bytes}B" )"
Example:
$ qalc -t -set "precision 3" 5264334820B
5.26 GB
It’s a very very powerful tool to have in shell scripting, as it can even simplify formulas, solve for unknowns, and many many more things.
$ qalc -t "e^(i*x)=-1"
x = 3.1415927
If you want a simpler, less heavy-weight solution, look at my other answer.