ByteScope

du

read-only

Reports 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

TokenMeaning
-kPrint 1024-byte units. This is already the default; passing it says so out loud.
-mPrint 1 MiB units.
-KPrint 512-byte units.
-bPrint apparent file size instead of blocks used.
-sOne total per argument, no per-directory rows.
-aInclude files, not only directories.
-d <n>Limit depth. The argument itself is depth 0, so `-d 1` prints it and its immediate children.
-hHuman-readable sizes. Convenient to read, imprecise to parse.
-cAppend 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.
-xRefuse 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.
-lCount 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 /sdcard

    The top two levels of shared storage, in KiB.

  • du -k -s /sdcard/DCIM

    One number for the camera roll.

  • du -k -d 1 /sdcard/Android/data

    Per-app data directories, where readability depends on the Android version.

  • du -k -d 1 /data/local/tmp

    A directory the shell owns outright, so no refusals appear and the total is known to be complete.

  • du -k -s -c /sdcard/DCIM /sdcard/Download

    Two 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	/sdcard

A 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

FieldMeaning
<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.
totalThe `-c` grand-total row. Its path is the literal word, not a directory.
du: <path>: Permission deniedOn 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

MessageMeaning
du: /sdcard/Android/data/com.example: Permission deniedThe 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 directoryThe 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.

Open Android Storage Analyzer

Related commands

Commands that turn up in the same session as du.

CommandWhat it does
dfReports free and used space per mounted filesystem.
findWalks a directory tree and prints what matches — big files, empty folders, leftovers.
dumpsys diskstatsFree space per partition, a disk write latency check, and a cached per-app breakdown of what is using storage.
rmDeletes 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.

Frequently asked questions

How do I run an adb shell command?

Switch on developer options and USB debugging on the phone, plug it in, then run adb shell <command> from a machine with platform-tools installed — or run adb shell on its own to get an interactive prompt and type the command without the prefix. If the command produces binary output, such as a screenshot, use adb exec-out instead of adb shell: the plain shell service attaches a terminal, which rewrites newline bytes and corrupts the data.

Do adb shell commands need root?

Not the ones on this reference. They run as the shell user, which can manage packages for its own Android user, read most dumpsys services, write the settings provider, inject input and read shared storage. Root is only needed for things this reference deliberately avoids — reading another app's private data (except through run-as on a debuggable build), writing system partitions, or reading /data/anr directly.

Why does the output look different on my phone?

Either the Android version or the manufacturer. Some flags and fields arrived in a specific release, and those are noted per command. The rest is OEM patching, which hits dumpsys hardest: a dump is debug output whose text is whatever the service author last printed, and manufacturers change it and backport their changes. Read fields by name rather than by position, and treat an unfamiliar shape as a difference rather than as a failure.

Can I run this from a browser instead of a terminal?

For many commands, yes — the Web ADB tools on this site talk to a phone over WebUSB from a Chromium-based desktop browser, with no SDK installed and nothing uploaded. Each page links to the tool that runs its command, when one exists. Two limits are worth knowing: only one program can hold the ADB interface at a time, so a desktop adb server has to be stopped first, and adb forward and adb reverse cannot work in a browser at all.

Would rather click than type? The Web ADB Toolkit runs commands like this from a browser over USB. Or browse every command.