Terminal · intermediate
Finding files with find
find walks the directory trees rooted at its starting points and evaluates an expression for every encountered pathname. The expression combines options, tests such as -type or -name, actions such as -print or -exec, and operators that control which parts run.
Why it matters
find can search by metadata and pass matching pathnames directly to other programs without parsing formatted directory listings. That makes it safe for unusual filenames, but only when shell quoting, expression precedence, and destructive actions are handled deliberately.
Mental model
How to reason about finding files with find
Read find as find STARTING_POINT... EXPRESSION. For each pathname, it evaluates the expression left to right with ! before implicit -a and -a before -o, short-circuiting when the result is known. If no output-affecting action is present, a matching expression gets an implicit -print.
Analogy
An inspector walks every aisle from each chosen entrance. For every item, the inspector evaluates a boolean checklist in precedence order; actions such as printing a label or handing the item to another worker run only when evaluation reaches them.
Examples
See the boundary, not just the happy path
Worked example · Find recent log files
find /var/log -type f -name '*.log' -mtime -7 -printThe shell quotes the pattern so find receives it literally. find then selects regular files whose names match and whose modification time falls within its last seven 24-hour buckets.
Worked example · Group alternative names
find . -type f \( -name '*.jpg' -o -name '*.png' \) -printEscaped parentheses make the name tests one grouped condition, so -type f applies to both alternatives. The quoted globs reach find instead of being expanded by the shell.
Worked example · Find large regular files
find . -type f -size +100M -printAdjacent tests have an implicit -a, so a pathname must be both a regular file and larger than 100 mebibytes in GNU find's M unit before it is printed.
Worked example · Batch a safe action
find src -type f -name '*.dart' -exec grep -lF TODO {} +-exec ... {} + passes matched pathnames directly as arguments in batches. It preserves whitespace and newlines in names and usually launches fewer processes than the \; form.
Avoid · Parsing ls output
for file in $(ls *.log); do process "$file"; doneCommand substitution and word splitting lose filename boundaries; find actions or shell globs preserve pathnames as arguments.
Common mistakes
Misconceptions to remove early
Leaving -name patterns unquoted
The shell can expand *.log before find starts, changing one intended pattern into unrelated command arguments.
Leaving -o alternatives ungrouped
Because -a binds more tightly than -o and evaluation short-circuits, find . -type f -name '*.c' -o -name '*.h' -print does not apply -type f or -print to both branches. Escape parentheses around the alternatives.
Treating -mtime as calendar dates
-mtime works with rounded 24-hour periods rather than calendar-day labels. Use -mmin for minute-scale age, -newermt in GNU find for date parsing, or explicit reference files when exact boundaries matter.
Adding -delete before validating selection
Print and review the same expression first, narrow its starting point, and understand that -delete is non-POSIX, irreversible, and implies depth-first traversal in GNU find.
Quick check
Can you predict the result?
1. Why should '*.json' be quoted in find . -name '*.json'?
- • So find receives the pattern instead of the shell expanding it in the current directory.
- • So JSON files become hidden.
- • Because -name accepts only quoted strings.
2. What advantage does -exec command {} + have over parsing printed pathnames?
3. How should you make -type f apply to both '*.jpg' and '*.png' alternatives?
4. What is the practical difference between -exec command {} \; and -exec command {} +?
Keep building
Related concepts
Put the concept to work