File size in human readable format

LinuxBash

Linux 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsomeguyView Question on Stackoverflow
Solution 1 - LinuxMelBurslanView Answer on Stackoverflow
Solution 2 - LinuxEvi1M4chineView Answer on Stackoverflow
Solution 3 - LinuxDavid J MerrittView Answer on Stackoverflow
Solution 4 - Linuxuser208145View Answer on Stackoverflow
Solution 5 - LinuxNour-eddinView Answer on Stackoverflow
Solution 6 - LinuxP.PView Answer on Stackoverflow
Solution 7 - LinuxEvi1M4chineView Answer on Stackoverflow