Skip to content

Finding Files


grep searches inside files for content. find searches the filesystem itself, for files matching almost any property you can describe — name, type, size, age — and then, if you want, it can act on whatever it finds. That last part is what makes find more than a search tool: it’s a way to describe a set of files once and run something against every single one of them, which is exactly the kind of thing you’ll lean on constantly once you’re managing a real system.

The Basic Shape

Every find command follows roughly this pattern:

find [<where-to-start>] [<what-to-look-for>]

Notice both are optional, this means find can works alone:

find

means find all the files/dirs recursively relative to path $PWD (current directory).

This prints every single file and directory under $PWD, recursively, with no filtering whatsoever. On its own that’s not very useful — the value comes from adding conditions.

Finding By Name

The most common starting point is -name, matching against a filename pattern:

find /home/you -name "*.txt"

This walks the entire tree under /home/you and prints only paths ending in .txt. The * here is a shell-style wildcard, not the regex * from the grep chapter — it means “any characters at all,” full stop, which is a genuinely different meaning from the regex * you just learned, even though it’s the same symbol. Worth keeping straight: grep patterns are regular expressions, find -name patterns are simpler shell-style globs.

-name is case-sensitive. For case-insensitive matching, use -iname instead:

find /home/you -iname "*.TXT"

Finding By Type

Sometimes you want only files, or only directories, regardless of name. -type handles this:

find /home/you -type f

f means regular files, d means directories. Combine it with -name to narrow further:

find /home/you -type d -name "backup*"

This finds only directories whose name starts with “backup” — a plain file happening to be named backup-notes.txt wouldn’t show up here.

Finding By Size

-size filters by file size, using a suffix to indicate the unit — c for bytes, k for kilobytes, M for megabytes, G for gigabytes.

find /home/you -size +100M

The + means “greater than” — this finds files over 100 megabytes. - means “less than,” and no sign at all means “exactly this size,” which is rarely what you actually want. This is one of the fastest ways to hunt down whatever’s quietly filling up a disk — a problem you’ll recognize from the note about /var filling up in the Filesystem Hierarchy Standard file.

Important

Don’t get confused between unit indicator -b and -c. They indicates two different things:

b (Blocks): Matches files based on blocks of 512 bytes. If a file is 1 byte or 400 bytes, it still occupies 1 block.

c (Bytes): Matches the exact file size down to the byte.

Finding By Age

-mtime filters by when a file was last modified, measured in whole days.

find /var/log -mtime +30

This finds files last modified more than 30 days ago — genuinely useful for spotting old log files or forgotten temporary data. Same sign convention as -size: +30 means older than 30 days, -30 means within the last 30 days.

Combining Conditions

By default, listing multiple conditions means all of them must match — an implicit “and.” You can also be explicit about logic with -a (and), -o (or), and ! (not):

find /home/you -type f -name "*.log" -size +10M

Finds files that are regular files, and end in .log, and are over 10MB — all three conditions apply together with no extra syntax needed.

find /home/you -type f -not -name "*.txt"

Finds every regular file except ones ending in .txt.

When mixing -o with other conditions, wrap groups in escaped parentheses to control how they combine, since find will otherwise apply its own default grouping, which is easy to get wrong:

find /home/you \( -name "*.txt" -o -name "*.md" \) -type f

This finds regular files ending in either .txt or .md — without the parentheses, find would interpret the conditions in a way that doesn’t mean what you’d expect.

Acting On What You Find: -exec

This is where find stops being just a search tool. -exec runs a command against every matching result, one at a time.

find /home/you -name "*.tmp" -exec rm {} \;

Break this down: {} is a placeholder that gets replaced with each matching file’s path, and \; marks the end of the command being executed. So for every file matching *.tmp, this runs rm on it individually — effectively, “delete every .tmp file under this directory,” expressed as a single line instead of a manual list.

You’re not limited to rm. Any command works:

find /home/you -name "*.log" -exec mv {} archived-logs/ \;

This moves every matching log file into archived-logs/, one at a time.

Warning

-exec ... rm {} \; is powerful and unforgiving in exactly the way rm -rf was in an earlier chapter — there’s no confirmation, and it runs against every match without pausing. Before running any find command with -exec rm, run the exact same find command without the -exec part first, and actually read the list of files it would have acted on. This is the same two-second habit from the rm file, applied to a tool that can now touch far more files in one command than you’d ever type by hand.

There’s also a dedicated shortcut for the deletion case specifically, -delete, which does the same thing more efficiently:

find /home/you -name "*.tmp" -delete

Same warning applies — test with a plain find (no -delete) first, every time.

-exec With {} + Instead Of {} \;

The \; form you just saw runs the command once per file, separately, every time. For a huge number of matches, that means launching the same program over and over — one rm invocation for each file, one mv invocation for each file, and so on.

{} + instead collects as many matching paths as it can and hands them to the command all at once, in as few invocations as possible:

find /home/you -name "*.tmp" -exec rm {} +

Functionally this deletes the same files as the \; version. The difference is efficiency: with \;, deleting 500 files means running rm 500 separate times; with +, find batches them into one or a handful of rm calls with many arguments each. For a small number of matches you won’t notice the difference. For a directory with thousands of matches, it’s the difference between a command that returns instantly and one that visibly grinds through each file one at a time.

One restriction worth knowing: {} can only appear once with the + form, and it must be the last argument before the command ends — you can’t scatter multiple {} placeholders through the command the way you technically can with \;. In practice this rarely matters, since the vast majority of -exec uses only need the placeholder once anyway.

Tip

As a rule of thumb: reach for {} + by default when the command supports taking multiple file arguments at once (rm, mv to a directory, chmod, etc.), and fall back to {} \; only when the command genuinely needs to run once per file individually.

Limiting How Deep It Searches

By default, find recurses through every subdirectory, no matter how deeply nested. -maxdepth caps that:

find /home/you -maxdepth 1 -type f

This looks only at files directly inside /home/you, ignoring anything in subdirectories — useful when you know roughly where something lives and don’t need (or want to wait for) a search of the entire tree underneath it.

Piping find Into grep

find and grep solve different problems, but they combine naturally: find locates files, grep searches file contents. A common pattern is using find to narrow down which files to look at, then piping the list into other tools, or using grep -r directly when you specifically want to search inside files rather than by filename. They’re complementary, not competing — reach for find when the question is about a file’s own properties (name, size, age, type), and grep when the question is about what’s written inside it.

What’s Next

Between navigation, file manipulation, editors, viewing, redirection, grep, sed and now find, you have everything needed to work comfortably in the filesystem. The next section moves to a different layer entirely: permissions — who’s actually allowed to read, write, or execute any of the files you’ve been creating this whole time.

Last updated on