← back to latest

Turn raw byte counts into human-readable sizes in any command's output

wc -c *.* | numfmt --field=1 --to=iec-i --suffix=B --padding=7

anatomy

wc -c *.*
Count bytes in every file whose name has a dot in it. The pattern skips directories like `project/`, which `wc` would otherwise complain about. It stands in for any command that prints exact byte counts and has no `-h` flag of its own: `wc`, `stat`, `find -printf`, an API response, a log line.
| numfmt
A GNU coreutils filter that reformats numbers in place. It reads lines, finds the number in the chosen field, rewrites it, and passes everything else through unchanged, so the file names survive.
--field=1
Which whitespace-separated field holds the number. 1 is the default, but saying it makes the command safe to edit. It takes `cut`-style lists and ranges like `2,3` or `2-4`, and `-d` switches the delimiter, so a CSV column works too.
--to=iec-i
Scale by powers of 1024 and print the two-letter binary prefix: Ki, Mi, Gi. Plain `--to=iec` uses the same 1024 math but prints a bare `K`, which looks identical to the 1000-based `--to=si`. The `i` removes the guessing.
--suffix=B
Append a literal string to every converted number, here a B for bytes, so `51Ki` reads as `51KiB`. Numbers that need no prefix get it too, which is why small files show `456B`.
--padding=7
Right-align the result in 7 characters. `numfmt` keeps the width of the original field when it can, but `wc` only pads to the widest number, so without this the shortened sizes come out ragged. A negative value left-aligns instead.

Sample output

  51KiB archive.tar.gz
  10KiB binary.dat
   456B data.json
 5.0MiB large-file.img
   456B link-to-data.json
   362B sales.csv
    12B script.sh
   993B server.log
   238B users.tsv
 5.1MiB total

The 5242880-byte image reads as 5.0MiB and the archive’s 52118 bytes as 51KiB. Names and the total line pass through untouched. link-to-data.json shows the same 456 bytes as data.json because wc follows the symlink and counts the target.

When you would reach for it

A script logs sizes in bytes, find -printf '%s' or stat hands you exact counts, or a JSON API reports "bytes": 3221225472 and you need to know whether that is a lot. Most tools that print bytes have no -h flag, and doing the division in bc for every row gets old fast. numfmt goes on the end of the pipe and converts whichever column you point it at, in either direction.

Gotchas

  • On the first line it cannot parse, numfmt prints an error, stops, and exits with status 2. Every line after that is lost. A header row like size name is enough to empty your output. Use --header to pass the first line through, or --invalid=warn to report bad lines and keep going.
  • It rounds up by default, the same way ls -h and du -h do, so 1025 bytes prints as 1.1K and 10241 bytes as 11K. Add --round=nearest for conventional rounding. It also gives up on decimals once the value reaches 10 in the chosen unit; --format='%.1f' keeps one decimal place.
  • si and iec give different answers for the same number. 5242880 bytes is 5.0M with --to=iec and 5.3M with --to=si. A drive sold as 4 TB holds about 3.64 TiB, and both numbers are right; numfmt --to=iec-i prints it as 3.7Ti because of the rounding above. Pick the base your audience expects and say which one with the -i form.
  • Thousands separators break input parsing: 1,234 fails with invalid suffix in input. Strip them first with tr -d ,, or leave the separators out of whatever generates the numbers.
  • numfmt is GNU only. On macOS, brew install coreutils installs it as gnumfmt. On Alpine, BusyBox has no numfmt; apk add coreutils provides it.

Variants

$ numfmt --from=iec 1.5G

Go the other way, for config files and flags that want exact bytes. Prints 1610612736. Use --from=si if you meant 1.5 billion

$ df -B1 --output=source,size,avail | numfmt --header --field=2,3 --to=si

Linux. Disk sizes the way the drive’s box counts them. Compare with df -h: the same filesystem reads 386G here and 359G there

$ numfmt --to=iec-i --suffix=B --round=nearest $(wc -c < large-file.img)

One file, no pipe. Redirecting into wc makes it print the bare count without a name, and command substitution passes that count to numfmt as an argument

lineage

numfmt is one of the youngest tools in GNU coreutils. Assaf Gordon contributed it, and it first shipped in coreutils 8.21 in early 2013, decades after `ls -h` and `du -h` had each grown their own private size formatting. The point was to stop reimplementing that logic in every tool and scripting language and give the shell one filter that does it for any column. The unit names it prints are older and more contested. The kibibyte, mebibyte, and the rest of the binary prefixes were defined by the IEC at the end of the 1990s to end the argument over whether a kilobyte is 1000 or 1024 bytes. Disk vendors had long counted in thousands while operating systems counted in 1024s, and the gap between the two numbers turned into marketing complaints and, by most accounts, more than one lawsuit. The new prefixes never fully caught on, which is why `numfmt` still lets you choose.