ByteScope

find

read-only

Walks a directory tree and prints what matches — big files, empty folders, leftovers.

Command
adb shell find
Commands
Files and storage
Risk
read-only

Syntax

find <path> [-type f|d] [-size +<N><suffix>] [-empty] [-maxdepth <n>] [-iname <pattern>] [-printf <format> | -exec stat -c <format> {} +]

Flags and arguments

TokenMeaning
-type f | dRegular files, or directories.
-size +<N><suffix>Bigger than N units. Always write the suffix: `c` bytes, `k` KiB, `m` MiB, `g` GiB — a bare number means 512-byte blocks and, on newer builds, something worse.
-emptyDirectories with nothing in them, or empty files.
-maxdepth <n>How deep to descend.
-iname <pattern>Case-insensitive name match. Quote the pattern so the device shell leaves it alone.
-printf <format>Android 11+Print chosen fields per match: `%s` size in bytes, `%p` path, `%T@` modification time.
-name <pattern>The case-sensitive counterpart of `-iname`. Shared storage compares names case-insensitively on modern builds, so the two agree there more often than they would on a desktop — which makes `-name` the wrong habit to carry over.
-print0Separate matches with a NUL byte instead of a newline. On shared storage that is not pedantry: a filename may legally contain a newline, and a newline-separated list turns one such file into two paths that do not exist.
-exec <cmd> {} +Run one command over as many matches as fit on a line. The `+` form keeps a whole scan to a handful of processes; the `;` form starts one per match, which over a cable is slow enough to notice and racy enough to matter.

Examples

  • find /sdcard -type f -size +100M

    Every file over 100 MiB in shared storage.

  • find /sdcard -type d -empty

    Empty directories — the litter left behind by uninstalled apps.

  • find /sdcard/Download -maxdepth 1 -type f -iname '*.apk'

    Installers you downloaded and forgot.

  • find /sdcard -type f -size +100M -print0

    The same hunt, NUL-separated, so a filename carrying a newline cannot be split into two paths that lead nowhere.

  • find /sdcard -type f -size +100M 2>&1

    Matches and refusals in one stream, which is the only form where an empty answer can be believed.

Reading the output

One matching path per line by default. To get sizes in the same pass, either `-printf` on Android 11 and later or `-exec stat -c … {} +` everywhere — a second `stat` per file is both slow over a cable and racy.

Output fields

FieldMeaning
<path>One match per line.
%sFile size in bytes.
%pThe path.
%T@Modification time as seconds, then a *nanosecond count* rather than a decimal fraction — `…0.5` means five nanoseconds.
find: bad arg '-printf'Android 10 or earlier. Fall back to `-exec stat`.
find: '<path>': Permission deniedA directory that was skipped, announced on stderr. Single-quoted, which a matched path never is.

What access it needs

Unprivileged, with one caveat that matters more than the permission itself. `find` has to open every directory it descends into, and the shell's reach is exactly the supplementary group list `adbd` grants it before dropping privileges — `sdcard_r`, `sdcard_rw`, `ext_data_rw`, `ext_obb_rw` — so shared storage walks and `/data/data` does not. The trap is not the refusal but where the refusal goes: an unreadable directory is announced on **stderr** while matches keep arriving on stdout, so `find /sdcard … 2>/dev/null` converts a partial scan into something that reads as a complete one. A large-file hunt that found nothing over 100 MiB with no error lines is a very different claim from one that found nothing while forty refusals were being thrown away, and only the second stream distinguishes them. Keep the two streams, or merge them deliberately, but do not discard the errors on a command whose answer decides what gets deleted next. Nothing here writes — `find` prints paths, and every destructive step afterwards is a separate `rm` you get to read first. The Android Storage Analyzer is built on that separation: one `find` per question, sizes gathered in the same pass so nothing has to be re-stat'ed, and a delete list assembled from what actually came back rather than from a wildcard nobody expanded on screen.

Failure messages, decoded

MessageMeaning
find: bad arg '-printf'Android 10 and earlier: that build's `find` has no `-printf` at all, and the complaint arrives before a single path is printed, so the result is empty rather than merely missing its size column. Fall back to the `-exec stat -c … {} +` form, which produces the same three fields on every release this family supports.
find: '/sdcard/Android/data/com.example': Permission deniedOne directory could not be opened; the rest of the walk is unaffected and keeps printing. The path is wrapped in single quotes, and that is the dependable way to tell this line apart from a match, because a matched path is always printed bare. If you are merging the streams, the quotes are your only marker.
find: bad -type 'x'`-type` takes exactly one letter — `f` for a regular file, `d` for a directory — and anything else stops the command before it walks anywhere. Nothing was searched, so an empty result here means the question was never asked.
find: '-size' needs 1 argA predicate that requires a value was written without one, usually because the surrounding shell swallowed the value while expanding something. Quote the argument. As with `-type`, this happens during the syntax pass, so no directory was ever opened.
find: big not [+-]NThe value handed to a numeric predicate is not a number with an optional leading sign. `-size` wants `+100M`, `-100M` or a bare count, and the word it quotes back is what your shell actually delivered — which is how a glob that matched a filename ends up standing where a threshold should have been.

Version and vendor notes

  • Android's `find` is toybox, not GNU, and `-size` behaves differently: the unit multiplier is applied only when the argument ends in a digit. With an explicit suffix the comparison is against bytes directly and there is no rounding up to whole units.
  • A bare `-size +100` is a bug waiting to happen. On Android 10 to 14 it means 100 times 512 bytes; on newer upstream code the internal units are negative, which makes `+N` match every file on the device.
  • `m` is 1024 squared, not a million. Only a trailing `d` would switch to powers of 1000.
  • Pass `-type f` explicitly. `-size` implying a regular file is newer than every shipping Android this family supports.
  • Refusals leave on stderr while matches leave on stdout, so silencing stderr rewrites "I was not allowed to look there" as "there is nothing there". It is the most expensive mistake available on this page, because the step after a scan is usually a deletion chosen from its result.
  • `-printf` arrived with Android 11's toybox and is still present on the `android14-release` and `android15-release` sources. Android 10 and earlier answer `find: bad arg '-printf'` and print nothing whatsoever, so treat an empty scan on an old phone as a failed command until the error stream says otherwise.

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 find.

CommandWhat it does
duReports how much disk space a directory tree occupies.
dfReports free and used space per mounted filesystem.
rmDeletes a file or a directory tree on the device.
dumpsys diskstatsFree space per partition, a disk write latency check, and a cached per-app breakdown of what is using storage.

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.