A find command that looks right can still match far more than you meant, and with -delete on the end there is no undo. In about 15 minutes you will build find commands that stay in scope, match what you intend and survive odd filenames.
The examples use GNU findutils 4.9.0. Allow a few extra minutes if you are adapting them to a large filesystem.
sudo is a last resort. It may get you into unreadable directories, but it also exposes more data and makes any action more dangerous. Only add it once you understand the permission error./.The first arguments are starting points; the expression follows them. Omit the starting point and GNU find searches the current directory, .. Keep that split visible, because a path beginning with - can otherwise be mistaken for part of the expression.
$ find /path/to/project -maxdepth 2 -type f -print
/path/to/project/README.md
/path/to/project/src/main.c
/path/to/project/src/notes.txt
-maxdepth 2 counts the starting point as depth zero and descends at most two levels.-mindepth 1 stops the starting directory itself matching.Checkpoint: Before adding any action, run with -print and check every path is inside the tree you meant. -print is also the default when no action is given, but writing it out makes the command easier to review.
-name matches the final path component and is case-sensitive; -iname ignores case. Quote wildcard patterns so the shell hands them to find instead of expanding them against the current directory first.
$ find /path/to/project -type f -name '*.c' -print
/path/to/project/src/main.c
-type f is a regular file, d a directory, l a symbolic link.-P.-L follows them. Put it before the starting point, only on purpose, and review the tree first: a link can lead outside the directory you meant to inspect.Group conditions with escaped parentheses. The shell never sees them, and the grouping means either suffix is accepted:
$ find /path/to/project -type f \( -name '*.c' -o -name '*.h' \) -print
No operator between tests means AND. -o means OR and binds more loosely than AND.
Warning: In -name 'one' -o -name 'two' -print, only the second branch has the print action. Group the whole condition when both matches should be acted on.
Time tests count complete 24-hour periods, not calendar days.
-mtime 0 matches files modified less than 24 hours ago.-mtime +7 matches files older than seven complete 24-hour periods.-mmin gives finer intervals.$ find /path/to/project -type f -mtime -1 -print
/path/to/project/src/main.c
Other useful read-only tests: -empty, -size +10M and -perm -u+x.
Warning: Check the exact predicate in man find before it goes into a cleanup job. Size suffixes and permission forms have specific meanings, and a plausible-looking command can select a much larger set than you expect.
Checkpoint: Save a representative listing before changing anything. Counting it modifies nothing:
$ find /path/to/project -type f -name '*.log' -print | wc -l
12
-prune stops find descending into a matching directory. Pair it with OR so the rest of the expression only runs on paths that were not pruned:
$ find /path/to/project -path '*/cache' -prune -o -type f -print
/path/to/project/README.md
/path/to/project/src/main.c
The left side matches the cache directory and prunes it; the right side prints ordinary files everywhere else. Unlike filtering the output afterwards, the cache contents are never visited.
Tip: If the directory name must also be excluded from another condition, add explicit grouping and test on a small tree first.
Newlines, spaces, quotes and wildcard characters are all valid in Unix filenames. Line-by-line output is for human eyes, not a safe way to pass data on. Use -print0 with a consumer that understands NUL separators:
$ find /path/to/project -type f -name '*.log' -print0 | xargs -0 -- printf '%s\n'
/path/to/project/app.log
/path/to/project/logs/error report.log
-- belongs to GNU printf. It stops a path starting with a hyphen being read as a printf option. Other commands have their own option rules, so check their manuals too.-files0-from, which reads NUL-separated paths from a file or standard input.To run a command on matches, prefer -execdir over the inherently insecure -exec form, where your environment has it:
$ find /path/to/project -type f -name '*.c' -execdir wc -l '{}' \;
8 ./main.c
{} is the pathname placeholder; an escaped semicolon ends the command.-execdir runs from the matched file's directory, which reduces the risk from a tree that changes under you.-execdir ... {} + passes several paths per invocation, if the target command accepts multiple operands.Warning: -execdir still runs a command. Review the command and its arguments before pointing it at untrusted directories.
Warning: -delete removes matching files and directories. It is not a move to a recycle bin and cannot be undone. Never test it first on a valuable tree, and never pair it with an accidentally broad starting point.
First swap the action for -print and inspect the complete result:
$ find /path/to/project -type f -name '*.tmp' -print
/path/to/project/build/output.tmp
Only after that review, run a tightly scoped delete. -delete implies depth-first traversal, so directory contents go before their parent directories:
$ find /path/to/project/tmp -type f -name '*.tmp' -delete
$ find /path/to/project/tmp -type f -name '*.tmp' -print
Checkpoint: The second command prints nothing if every matching file was removed.
Recovery: If the pattern was wrong, restore from your backup or snapshot. In current findutils releases a failed -delete gives a non-zero exit status, but find may carry on with other paths, so check the status and any diagnostics.
Exit status zero means every file was processed successfully. Non-zero means errors occurred and the results are not complete. Capture it immediately in a script:
$ find /path/to/project -type f -print > /tmp/project-files.txt
$ status=$?
$ printf 'find status: %s\n' "$status"
find status: 0
sudo only when policy permits and you know why access is needed.-ignore_readdir_race suppresses some of those diagnostics, but hiding an error does not prove a complete result.-quit after the action, as below.-D help lists the installed debug modes. They are not guaranteed to stay compatible between findutils releases, so do not automate around their wording.$ find /path/to/project -type f -name 'README.md' -print -quit
/path/to/project/README.md
-L.-print and its exit status checked.