find, one lesson per page
Twenty-nine lessons for finding files on Linux: the expression grammar every command shares (tests, operators, actions), quoting patterns so the shell does not eat them, -type and -size windows, the day arithmetic behind -mtime and -newer, escaped parentheses and the -a versus -o precedence trap, -maxdepth and -prune to stop the crawl, the whole -exec versus -print0 versus -delete decision with the race condition underneath it, GNU versus POSIX differences, the paths-must-precede-expression error, and the dry-run habit that makes destructive finds safe.
A diagram, the map, and one thing to try this week. That's a page.
find, one lesson per page
Twenty-nine lessons for finding files on Linux: the expression grammar every command shares (tests, operators, actions), quoting patterns so the shell does not eat them, -type and -size windows, the day arithmetic behind -mtime and -newer, escaped parentheses and the -a versus -o precedence trap, -maxdepth and -prune to stop the crawl, the whole -exec versus -print0 versus -delete decision with the race condition underneath it, GNU versus POSIX differences, the paths-must-precede-expression error, and the dry-run habit that makes destructive finds safe.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
Every fact in this book is as the official sources state it, fetched and read during this build: the find(1) and xargs(1) man pages hosted at man7.org (the Linux man-pages project, documenting GNU findutils), the POSIX.1-2017 (Issue 7) find(1) specification at pubs.opengroup.org, and the Deleting Files chapter (10.1) of the GNU findutils manual Finding Files at gnu.org. GNU-specific defaults and extensions are labelled as those sources state them. Demand evidence from live beginner threads, reconfirmed at dispatch; no facts are sourced from Reddit or StackExchange. An independent guide, not affiliated with or endorsed by the GNU Project or The Open Group.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodgkiss.net
Contents
Per find(1) (man7.org, GNU findutils): find searches the directory tree at each starting-point, evaluating how each file's name or attributes compare to the given expressions, until the outcome is known; if no starting-point is specified, '.' is assumed; if no expression is given, the expression -print is used.
The grammar of a find
Every find command is two zones: starting-points, then one expression. The walk starts at each path; the expression is evaluated for every file it meets.
Per find(1), if no starting-point is given, '.' is assumed. If no expression is given, the expression -print is used. So bare find lists everything below the current directory.
Hold this shape and every flag on the man page has a slot to land in.
This week, read three find commands you already use and label each word: starting-point, test, operator, action.
Per find(1) (man7.org) -name: don't forget to enclose the pattern in quotes in order to protect it from expansion by the shell; per POSIX find(1), the pattern matching notation applies and this is a matching operation, not an expansion.
Quote the pattern
The shell sees your command before find does. Unquoted *.txt is expanded by the shell into the matching filenames in the current directory.
Per find(1): don't forget to enclose the pattern in quotes to protect it from expansion by the shell. What find receives must still be the pattern.
Single quotes are the habit: they stop every expansion, and find(1) itself recommends them.
Type an unquoted find -name *.txt once in a directory that already has a .txt file and read the error. Then quote it.
Per find(1) (man7.org) OPERATORS: two expressions in a row are taken to be joined with an implied -a; expr2 is not evaluated if expr1 is false; in the expression list intro, -o means logical OR and -a means logical AND, and where an operator is missing, -a is assumed.
The AND that binds
Per find(1), two expressions in a row are joined with an implied -a, and the second is not even evaluated when the first is false.
So find . -name '*.log' -type f means name matches and the file is regular. That silent glue is why stacked tests narrow a search.
Every space between tests is the word and. Say it and long commands read like sentences.
Take your longest find this week and insert the word and between every test. Then delete the word. Nothing changed.
Per find(1) (man7.org) -maxdepth levels: descend at most levels (a non-negative integer) levels of directories below the starting-points; -maxdepth 0 means only apply the tests and actions to the starting-points themselves; -mindepth 0 means apply tests and actions to all files; both are listed as GNU options in the CONFORMING TO table; positional options affect only tests occurring later on the command line.
Stop the crawl
-maxdepth is a fence: descend at most n levels below the starting-points. Per find(1), -maxdepth 0 applies the tests to the starting-points themselves, and it is a GNU option.
It reads like a test but behaves like a setting, positioned before the tests it should shape: positional options affect only tests later on the command line.
A one-level walk of the current directory is the cheapest speed-up in the whole tool.
Add -maxdepth 1 to your three most-used finds this week and time them against the unbounded versions.
Per find(1) (man7.org) -exec: -exec command ; executes command; the string '{}' is replaced by the current file name being processed; both the braces and the semicolon might need to be escaped (with a backslash) or quoted to protect them from interpretation by the shell; per POSIX find(1), with the semicolon form the utility is invoked once for each pathname.
Act on every match
-exec turns matches into work. The braces are replaced by the current file name; per find(1), the semicolon and the braces may both need escaping or quoting, because the shell wants them for itself.
Per POSIX find(1), with the semicolon form the utility runs once for each pathname: a thousand files, a thousand commands.
It is a per-file sentence with a slot for the name. Escape the semicolon or the shell ends the command early.
Compress one folder's old logs with -exec this week. Count the processes with a dry -print run first.
Per find(1) (man7.org): if no expression is given, the expression -print is used; where an operator is missing, -a is assumed; the -print action is performed on all files for which the whole expression is true; the manual's own examples demonstrate chaining tests, grouping with escaped parentheses, pruning, and testing with -print before -delete.
One habit to keep
Every page of this book folds into one loop: narrow, review, act. Narrow with tests, operators and escaped groups. Review with -print. Act by swapping the action word in.
Per find(1), the pieces of the loop are the defaults and assumptions of the tool itself: -print when no action is given, -a where an operator is missing.
The command you can explain is the command you can trust.
Take one real task this week, run all three stations of the loop on paper first, then in the shell.