du for macOS that knows about iCloud.
When iCloud Drive's Optimize Mac Storage evicts a file, the file still shows up in Finder and in ls
with its full size, but it takes no space on disk, so du reports 0 for it. icdu counts those files
as if they were still stored locally: it prints what du would print on a machine where nothing had been evicted.
$ du -sh ~/Documents
276M /Users/you/Documents
$ icdu -sh ~/Documents
1.2G /Users/you/DocumentsEverything else is meant to be exactly du: same options, same output format, same order, same exit codes.
If you want plain du numbers, icdu --local gives them to you.
npm install -g icdumacOS only (it refuses to install elsewhere). Needs Node.js 18 or newer.
Use it like du:
icdu -shc ~/Documents ~/Desktop ~/Library/Mobile\ Documents
icdu -d1 -h ~/Library
icdu -Sh -d1 ~/Library # biggest folders first
icdu -shcS ~/Documents ~/Desktop # one line per folder, biggest firstLike du, -h and --si print the decimal separator of your locale (4,1M with a Turkish or German LC_NUMERIC).
Options follow macOS du(1): -A -a -B -c -d -g -H -h -I -k -L -l -m -n -P -r -s -t -x --si and the
BLOCKSIZE environment variable. icdu --help lists them.
Extras that are not in du:
-S |
Sort the printed lines by size, largest first (like ls -S); add -r for smallest first (-Sr). Equal sizes keep du's order and -c's total stays last. |
--local |
Count only what is stored on this Mac, exactly like du. Also ICDU_LOCAL=1. |
--plain |
No live table, no progress display. Also ICDU_PLAIN=1, or when CI is set. |
--help, --version |
icdu -s a b c d prints one line per operand. In a terminal each line shows a spinner until its
directory has been scanned and then fills in, so quick directories appear without waiting for slow ones.
When everything is done, the screen holds exactly what du would have printed. Two dim notes may appear:
0B /etc -> private/etc (a symlink; du does not follow it either, hence 0)
1.9G /usr ! 1 unreadable (permission errors: the number is incomplete)
Entries that cannot be read (permission errors, protected macOS folders) are counted instead of listed one by one:
the table shows ! N unreadable, and listings end with a single icdu: N entries could not be read line on stderr.
To see which paths failed, run again with --plain, or redirect stderr (icdu ... 2>errors.txt keeps the full list).
Errors for an operand itself (for example a path that does not exist) are always printed.
For listings (-a, -d1, no -s) a progress counter is shown on stderr while scanning. Piped or redirected
output is plain du-style text; nothing decorative is written. --plain turns the decorations off on a terminal too.
Operation not permittedon folders like/Library/Application Support/com.apple.TCCor/Library/Trialis macOS protecting them. Grant your terminal app Full Disk Access (System Settings -> Privacy & Security) to include them, or ignore it: the number is then a lower bound, which is whatdugives you too.Permission deniedis an ordinary permission problem; run withsudoif you need those directories.- A directory shows
0Band the line ends with-> somewhere: it is a symlink, whichdudoes not follow without-Hor-L.
- Every file and directory is examined with
lstat. Files and directories are never opened or read, so nothing is downloaded from iCloud. - A regular file with a size but no allocated blocks is either evicted (marked
UF_DATALESS) or a fully sparse file. The flag is read withstat(1)in a few bulk calls. Evicted files are chargedceil(size / 4096)APFS blocks, which is what they occupy once downloaded. Sparse files stay at 0, like indu. - Hard links are counted the way
ducounts them, including its habit of forgetting a file once all its links were seen. - The tree is scanned with 16 concurrent filesystem calls and printed in
du's order afterwards.
icdu is checked against /usr/bin/du by differential tests: for ordinary trees, and with --local for iCloud
trees, the output must be byte-identical.
Faster than du on big trees (about 6 s against 7-9 s for a 44 GB /Applications with 566k entries) and slower on
small, fast ones (about 0.4 s against 0.2 s for /usr), because of Node's start-up time and a higher per-entry cost.
du uses a bulk system call that Node cannot reach. Cold caches favor icdu: the parallel scan hides disk latency.
- macOS only, tested on macOS 26 (Apple silicon, APFS) with Node 18, 20, 22 and 25 only. Other macOS versions may
differ in
du -h,-t,-Iandstat(1)details; runnpm testto compare with your owndu. - Memory: the whole tree is held in memory, about 0.8 KB per entry (
-s) or 1.5 KB (-a). Scanning/on a full disk can exceed Node's default heap. - Time Machine: directories hard-linked by Time Machine backups are not de-duplicated like
dudoes. - Old iCloud stubs: the
.name.icloudplaceholders of pre-Sonoma macOS are not recognized. - File names that are not valid UTF-8 (possible on exFAT/NTFS/SMB volumes, impossible on APFS) are not handled.
- If a file's flags cannot be read during the scan (it vanished, or its name contains a line break), a file without blocks is assumed to be evicted, which can over-count a fully sparse file.
- Some
dubehaviors were reproduced from observation, not source: a negative-thides every directory,-Imatches the base name only, and a symlink cycle under-Lends the run. Warnings are printed while scanning, so their order can differ fromdu. - The live table is made for ordinary terminals; very wide characters in names or resizing the window while it runs
can garble it. Use
--plain.
npm install
npm test # builds, then runs the tests; needs macOSSome tests use evicted files from your own ~/Documents or ~/Desktop and are skipped when there are none.
The tests never modify or delete anything outside temporary directories.
Bug reports, du mismatches and small pull requests are welcome, see CONTRIBUTING.md.
Changes are listed in CHANGELOG.md; security issues go through SECURITY.md.
MIT, see LICENSE.