I first needed this while running GNU ddrescue on a friend’s dying hard drive. Bad reads often show up as null (0x00) padding in the image. Counting non-null bytes was a rough “how much did we salvage?” signal — and, stacked against their memory of how full the disk had been, a ballpark % recovered.
Same idea shows up elsewhere: sparse dumps, padded blobs, firmware images, or any file where “how much real payload is in here?” matters more than stat size.
Simple count#
Strip nulls with sed, then count bytes with wc -c:
sed 's/\x00//g' /path/to/file | wc -cThat is the whole trick: delete \x00, measure what remains. On a rescue image, treat the result as salvaged payload bytes (not a forensic guarantee — legitimate data can contain nulls too — but good enough for a recovery estimate).
Large files need progress#
Rescue images are huge. The pipeline can run a long time with no feedback. Put pv in front so you see throughput and ETA:
pv /path/to/file | sed 's/\x00//g' | wc -cpv reads the file, shows how much has been consumed, and feeds sed the same stream.
Two meters: input vs non-null output#
If you want bytes read and non-null (salvaged) bytes so far side by side, use two named pv stages (-N) and keep the counters on screen (-c):
pv -N in -c /path/to/file | sed 's/\x00//g' | pv -N out -c | wc -c| Meter | Meaning |
|---|---|
| in | Progress through the image / original file |
| out | Non-null bytes emitted after sed |
wc -c | Final non-null total when the pipe finishes |
Rough recovery % (if you trust a “disk was about F% full” memory):
recovered ≈ (non_null_bytes / image_size) / (F / 100)Or simpler: compare non_null_bytes to F% × original_disk_size when you know the drive capacity.
Notes#
- Needs a
sedthat understands\x00in the replacement pattern (GNU sed is the usual case on Linux). - This streams; it does not load the whole file into a single string. Still,
sedbyte-rewriting is not the fastest tool for multi‑GB rescue images — fine for ops one-offs, not a benchmark harness. - For a quicker strip on many systems,
tr -d '\000'can replacesedin the same pipeline shape if you only care about deleting nulls. - Nulls ≠ only failed reads; use this as an estimate, not proof of file-level recovery.
