A standard tool to convert a byte-count into human KiB MiB etc; like du, ls1

Solution:

No, there is no such standard tool.

Since GNU coreutils 8.21 (Feb 2013, so not yet present in all distributions), on non-embedded Linux and Cygwin, you can use numfmt. It doesn't produce exactly the same output format (as of coreutils 8.23, I don't think you can get 2 digits after the decimal points).

$ numfmt --to=iec-i --suffix=B --padding=7 1 177152 48832200 1975684956
     1B
 173KiB
  47MiB
 1.9GiB

Many older GNU tools can produce this format and GNU sort can sort numbers with units since coreutils 7.5 (Aug 2009, so present on modern non-embedded Linux distributions).


I find your code a bit convoluted. Here's a cleaner awk version (the output format isn't exactly identical):

awk '
    function human(x) {
        if (x<1000) {return x} else {x/=1024}
        s="kMGTEPZY";
        while (x>=1000 && length(s)>1)
            {x/=1024; s=substr(s,2)}
        return int(x+0.5) substr(s,1,1)
    }
    {sub(/^[0-9]+/, human($1)); print}'

(Reposted from a more specialized question)


As of v. 8.21, coreutils includes numfmt:

numfmt reads numbers in various representations and reformats them as requested.
The most common usage is converting numbers to / from human representation.

e.g.

printf %s\\n 5607598768908 | numfmt --to=iec-i
5.2Ti

Various other examples (including filtering, input/output processing etc) are presented HERE.


In addition, as of coreutils v. 8.24, numfmt can process multiple fields with field range specifications similar to cut, and supports setting the output precision with the --format option
e.g.

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
tx: 175.782Ki rx: 1.908Mi

Here's a bash-only option, no bc or any other non-builtins, + decimal format and binary units.

# Converts bytes value to human-readable string [$1: bytes value]
bytesToHumanReadable() {
    local i=${1:-0} d="" s=0 S=("Bytes" "KiB" "MiB" "GiB" "TiB" "PiB" "EiB" "YiB" "ZiB")
    while ((i > 1024 && s < ${#S[@]}-1)); do
        printf -v d ".%02d" $((i % 1024 * 100 / 1024))
        i=$((i / 1024))
        s=$((s + 1))
    done
    echo "$i$d ${S[$s]}"
}

Examples:

$ bytesToHumanReadable 123456789
117.73 MiB

$ bytesToHumanReadable 1000000000000 # '1TB of storage'
931.32 GiB                           #  1TB of storage

$ bytesToHumanReadable 
0 Bytes

$ bytesToHumanReadable 9223372036854775807
7.99 EiB

Should perform well on any version of Bash out there (including MSYSGit's Bash for Windows).