du
read-onlyReports how much disk space a directory tree occupies.
- Command
- adb shell du
- Commands
- Files and storage
- Risk
- read-only
Syntax
du [-k] [-m] [-K] [-b] [-a] [-s] [-d <depth>] [-h] <path>Flags and arguments
| Token | Meaning |
|---|---|
| -k | Print 1024-byte units. This is already the default; passing it says so out loud. |
| -m | Print 1 MiB units. |
| -K | Print 512-byte units. |
| -b | Print apparent file size instead of blocks used. |
| -s | One total per argument, no per-directory rows. |
| -a | Include files, not only directories. |
| -d <n> | Limit depth. The argument itself is depth 0, so `-d 1` prints it and its immediate children. |
| -h | Human-readable sizes. Convenient to read, imprecise to parse. |
| -c | Append a grand-total row after everything else. Its path column holds the literal word `total` rather than a directory, which is how a parser tells it apart from a real row without counting lines. |
| -x | Refuse to cross onto another filesystem. That matters on a phone: shared storage and the userdata partition are separate mounts, and a walk that begins above both can otherwise count the same blocks twice under two different names. |
| -l | Count a hard-linked file once per link instead of once altogether. The default deduplicates by inode, which is the honest answer to "how much would I get back". |
Examples
du -k -d 2 /sdcardThe top two levels of shared storage, in KiB.
du -k -s /sdcard/DCIMOne number for the camera roll.
du -k -d 1 /sdcard/Android/dataPer-app data directories, where readability depends on the Android version.
du -k -d 1 /data/local/tmpA directory the shell owns outright, so no refusals appear and the total is known to be complete.
du -k -s -c /sdcard/DCIM /sdcard/DownloadTwo totals and a summed `total` row, which is cheaper than adding the numbers by hand and cannot round differently from them.
Reading the output
Sample output
12048 /sdcard/DCIM/Camera
12456 /sdcard/DCIM
348920 /sdcardA size, a literal tab, and a path — one row per directory, in post-order, so children appear before the parent that contains them. The separator is a tab and only the first one counts, which is what lets a filename contain spaces (and, legally, tabs) without breaking anything. Never split these rows on whitespace.
Output fields
| Field | Meaning |
|---|---|
| <size> | In whichever unit the flags asked for; 1024-byte blocks by default. |
| <tab> | The one and only field separator. |
| <path> | The directory, exactly as it is on the device. |
| total | The `-c` grand-total row. Its path is the literal word, not a directory. |
| du: <path>: Permission denied | On stderr, interleaved with the table. A directory that was skipped, which makes every enclosing total an underestimate. |
What access it needs
No root, no `adb root`, and nothing to switch on in developer options beyond USB debugging. What decides how much of the tree you actually get to measure is the group list `adbd` gives the `shell` user before it drops privileges: `packages/modules/adb/daemon/main.cpp` names `sdcard_r`, `sdcard_rw`, `ext_data_rw` and `ext_obb_rw` among the supplementary gids, which is precisely why shared storage — and on most builds `/sdcard/Android/data` with it — measures cleanly from a cable while an ordinary app on the same phone is refused both. What the shell still cannot open is app-private storage under `/data/data` and `/data/user/0`; aiming `du` there yields a page of refusals rather than a number, and the way in without root is `run-as` against a debuggable package. Nothing here writes: the command opens directories, sums the block counts the kernel reports, and prints. The refusals it meets on the way are not fatal, either — the directory walker reports each unreadable directory and carries on — so a total that arrived with errors mixed into it is a *partial* total, and the exit status is the only place that says so out loud. The Android Storage Analyzer drills one level at a time for exactly that reason: a refused subtree stays visible as a hole in the picture instead of being quietly folded into whatever contains it.
Failure messages, decoded
| Message | Meaning |
|---|---|
| du: /sdcard/Android/data/com.example: Permission denied | The walker could not open that directory, named it on stderr and continued. Every row already printed remains correct; the enclosing directory's total is simply short by whatever was inside. On Android 11 and later, which of these per-app directories the shell may open is both version- and vendor-dependent, so a mixture of totals and refusals is the ordinary answer rather than a broken run. |
| du: /sdcard/nope: No such file or directory | The argument itself could not be examined, which is a different failure from the one above: nothing was measured and not a single row was printed. Almost always a path assembled with the wrong spelling — shared storage compares names case-insensitively through the FUSE layer on modern builds, but nothing corrects a name that is simply wrong. |
| du: Missing argument to -d (see "du --help") | `-d` wants the depth as a separate word. Toybox's option parser raises this before `du` itself runs, so nothing was measured at all. The `(see "… --help")` suffix is appended by that parser, which makes it a reliable marker: a line carrying it is an argument mistake, never a filesystem one. |
| du: -d < 0 (see "du --help") | `-d` is declared `d#<0`, a number with a floor of zero, so a negative depth is out of range and nothing is measured. It catches people reaching for `-d -1`, because -1 is exactly the value `du` uses internally for "no limit" — but the unlimited walk is what you get by leaving `-d` off altogether, not by passing that number in. Worth knowing what does *not* produce a line: `-h`, `-k`, `-K` and `-m` are a switch-off group rather than an exclusive one, so `du -k -h` is accepted in silence and the flag you wrote last simply wins. |
Version and vendor notes
- The default unit is a 1024-byte block and the division rounds *up*, so a one-byte file reports as 1 and a directory of ten thousand tiny files reports far more than the bytes inside it. That is genuinely how much disk it occupies — which is the right question when you are deciding what to delete — but it is not the sum of the file sizes. `-b` answers the other question.
- `-h` is convenient and ambiguous: only three significant digits survive, and above the kilobyte suffix the letters are identical whether the divisor was 1000 or 1024. Ask for block units whenever the number matters.
- `du: …: Permission denied` interleaved in the output is a real finding, not noise. On Android 11 and later, shell access to per-app directories under shared storage is version- and manufacturer-dependent.
- The supplementary groups `adbd` grants the shell — `sdcard_r`, `sdcard_rw`, `ext_data_rw`, `ext_obb_rw` — are what make shared storage measurable from a cable at all. Nothing inside `du` requests a permission and nothing can grant it one.
- A refused directory does not end the walk, so a run that printed errors also printed totals, and those totals are short by exactly what it could not open. Collect stderr or the shortfall is invisible.
We have a UI for this
Android Storage Analyzer runs this command over USB and parses what comes back — a Chromium browser, no SDK, nothing uploaded.
Related commands
Commands that turn up in the same session as du.
| Command | What it does |
|---|---|
| df | Reports free and used space per mounted filesystem. |
| find | Walks a directory tree and prints what matches — big files, empty folders, leftovers. |
| dumpsys diskstats | Free space per partition, a disk write latency check, and a cached per-app breakdown of what is using storage. |
| rm | Deletes a file or a directory tree on the device. |
Checked against AOSP, toybox and kernel sources while writing the parsers behind our Web ADB tools. Where a behaviour genuinely varies by Android version or manufacturer, this page says so instead of giving a number that would be wrong on half the phones out there.