TechEarl

How to Find and Delete Files Safely with find -delete

find -delete removes every matched file with no confirmation and no undo. The safe pattern is to write the command with -print first, eyeball the list, then swap -print for -delete. Plus the directory-depth-first trap, when to use -exec rm instead, and the find -delete vs xargs rm -f tradeoff.

Ishan KarunaratneIshan Karunaratne⏱️ 10 min readUpdated
find -delete removes every matched file with no confirmation. The safe -print-first dry-run pattern, depth-first directory deletion, when to use -exec rm vs xargs rm -f, and the BSD vs GNU differences.

find /path/to/dir -type f -name '*.log' -delete is the shortest way to delete every matching file with one command. It is also the easiest way to nuke a directory tree you didn't mean to.

There is no confirmation prompt. There is no -i interactive mode like rm -i. There is no trash bin. The only safety net is the human at the keyboard, which means the only safe pattern is: write the command with -print first, eyeball the list, then change -print to -delete. That two-step is the difference between a clean script and a 3am incident.

Set your values

Try it with your own values

Set your OS, search path, and the name pattern. Every find example below updates with your values.

The safe two-step pattern

Always do this. Every time. No exceptions.

Step 1: Dry run with -print. See exactly what would be deleted:

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -print

Read the output. Confirm it's only what you expect. Look for unexpected paths, unexpected file types, anything that wasn't on your mental checklist.

Step 2: Swap -print for -delete. Same command, one word different:

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -delete

That's it. There's no confirmation, no progress bar, no undo. Once you press Enter, the files are gone.

If you're nervous, run a count first: find ... -print | wc -l to know how many files you're about to remove.

Delete files older than N days

The single most common find-delete pattern: clean up stale files. -mtime +N matches files older than N days; combine with -delete:

bash· Linux (GNU)
# Dry run
find :search_path -type f -mtime +:days -print
# Delete
find :search_path -type f -mtime +:days -delete

Typical use: clearing /tmp/cache, rotating log archives, pruning Docker build cache. See find files modified in the last N days for the full -mtime reference including the off-by-one rules.

Delete files larger than a size

Same shape, different filter:

bash· Linux (GNU)
find :search_path -type f -size +100M -print  # dry run
find :search_path -type f -size +100M -delete

Useful for finding and removing oversized core dumps, build artifacts, video files in the wrong directory. See find files larger than a size for the full -size reference.

Delete empty directories (the depth-first trap)

This is where most beginner cleanup scripts fail. To delete empty directories, you need find to descend depth-first so children are removed before parents:

bash· Linux (GNU)
find :search_path -depth -type d -empty -delete

The -depth flag flips find's default traversal order so that parents are processed after their children. Without -depth, find tries to remove a parent first, sees it's not empty (because the children haven't been processed yet), and skips it. Then it removes the children. The parent is now empty but find has already moved past it.

-delete implies -depth automatically in GNU find when combined with -type d, but being explicit is safer for cross-platform scripts.

Two-pass cleanup: files then empty directories

The standard cleanup recipe: first remove files matching a pattern, then remove any directories that are now empty.

bash· Linux (GNU)
# Pass 1: delete files
find :search_path -type f -name ':pattern' -delete
# Pass 2: delete now-empty directories
find :search_path -depth -type d -empty -delete

This is the pattern any cleanup script for build artifacts, log rotations, or temp-file pruning should follow.

find -delete vs -exec rm vs xargs rm -f

Three ways to delete matched files, in roughly the order I reach for them:

ApproachProsConsWhen to use
find ... -deleteFastest. Built in. Handles weird filenames safely.No control over what happens per-file. Implies -depth for directories.Default for simple deletes
find ... -exec rm {} +Same speed as -delete. Works with any tool, not just rm.Slightly more verbose.When you want rm -f (force) or rm -v (verbose)
find ... -print0 | xargs -0 rm -fSame speed. Works with shell pipelines.Adds a pipe to NUL-delimited safety: requires -0 on both sides.When you need shell features around the delete
find ... -exec rm {} \;Works everywhere.Forks one rm per file. Slow on large trees.Avoid unless the command can only accept one arg at a time

Detailed behavior of each:

bash· Linux (GNU)
# The four ways to delete
find :search_path -type f -name ':pattern' -delete
find :search_path -type f -name ':pattern' -exec rm {} +
find :search_path -type f -name ':pattern' -print0 | xargs -0 rm -f
find :search_path -type f -name ':pattern' -exec rm {} \;

For 99% of cleanup scripts, plain -delete is the right choice. The other forms are useful when you need rm's extra flags (-v, -i) or when the deletion is conditional on something a shell can compute that find can't express.

macOS BSD vs GNU find -delete

Most behavior matches between BSD and GNU, with a few corner cases:

FeatureGNU findBSD find (macOS default)
-delete existsYesYes
-delete implies -depth for directoriesYes (since GNU find 4.6+)Yes
-delete reports per-file errorsYesYes
Permission errors silence successful deletesNo (-delete continues)No (continues)
Exit code on partial failure1 (some deletes failed)1
-delete -print togetherWorks (deletes then prints)Works

The single behavior I've been bitten by: find -delete on a directory that contains files you don't have permission to delete will silently leave the files in place but still attempt the directory delete (which then fails). Run a -print first to see what permissions look like, or use find ... -exec rm -f to surface the permission errors explicitly.

Common find -delete mistakes

1. Skipping the -print dry run. This is the #1 cause of "I just nuked my home directory" stories. Always preview before deleting.

2. Forgetting -type f. Without -type f, find evaluates directories against the name filter too. find /tmp -name '*.bak' -delete will try to remove any directory called something.bak, fail because it's not empty, and confuse you with errors. Always anchor with -type f for file-only operations.

3. Running cleanup against / or ~. Even with -name, a typo in the pattern can match more than you expect. Always anchor at a specific subdirectory. find / is essentially never the right path for a -delete operation.

4. Forgetting to quote the pattern. find . -name *.bak -delete lets the shell expand *.bak against the current directory. If there's one file matching, find gets that filename as the pattern (not a glob). If there's none, find gets the literal *.bak and a "paths must precede expression" error. Always quote: -name '*.bak'.

5. Using -exec rm {} \; on huge trees. Each match forks a new rm process. On a tree with 100,000 matches, that's 100,000 forks (slow). Use -exec rm {} + (batches) or -delete (no fork at all).

6. Forgetting that -delete doesn't follow symlinks by default. A symlink matched by -delete removes the symlink, not its target. If you want to delete what symlinks point to, you need -L before the search path or different logic entirely.

7. Running with sudo when you don't need to. Files you own can be deleted without sudo. Running sudo find ... -delete widens the blast radius if the find expression is wrong. Use sudo only when the files genuinely require root, and even then, dry-run first.

8. Not knowing the order of test evaluation. find evaluates expressions left to right. find . -delete -name '*.bak' deletes everything in the tree because -delete runs before the name filter. The correct order is find . -name '*.bak' -delete. Tests first, actions last.

When NOT to use find -delete

Reach for a different approach when:

  • You need a confirmation prompt per file. Use find ... -exec rm -i {} \; (interactive rm). Slow but safe.
  • You need to log what was deleted. Use find ... -exec rm -v {} + (verbose rm) and redirect to a log file. -delete itself is silent.
  • You need to move files to a trash directory rather than delete. find ... -exec mv {} /path/to/trash/ \; moves matched files. Or use trash (Linux) or trash-cli (cross-platform) which talks to the system trash.
  • You're working on a remote system over a slow link. find runs on the remote side; -delete is local to that side. But if you're piping the file list back over SSH to inspect, the round-trips dominate. Run find on the remote.
  • You need atomic batch deletes (all or nothing). find deletes one file at a time. If the script is interrupted, you get partial results. For atomic behavior, list-then-delete via a temp file:
    code
    find ... -print0 > /tmp/del-list
    xargs -0 rm -f < /tmp/del-list
    

See also

FAQ

TagsfinddeletermCLILinuxmacOSBSDCleanup Scripts
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years across software, Linux systems, DevOps, and infrastructure — and a more recent focus on AI. Currently Chief Technology Officer at a tech startup in the healthcare space.

Keep reading

Related posts

Use find -type d -empty to list empty directories and find -type f -empty for empty files. The -depth trap for deleting nested empty trees, the hidden-file gotcha, the safe two-pass cleanup, and BSD vs GNU find notes.

How to Find (and Delete) Empty Directories and Files

find . -type d -empty lists every empty directory; find . -type f -empty lists every empty file. The catch is what 'empty' means (a hidden file makes a directory not empty) and the -depth trap that lets find -delete collapse whole nested empty trees in one pass. The flag reference, the safe two-pass cleanup, the BSD vs GNU notes, and the mistakes that bite.

rsync files modified in the last N days by piping find -print0 into rsync --files-from=- --from0. The relative-path gotcha, the dry run, BSD vs GNU notes, and when rsync filters replace find.

How to rsync Only the Files find Selected

rsync has no native time filter, so the standard trick is to let find pick the files and feed the list to rsync. The one-liner is find ... -print0 | rsync --files-from=- --from0, and the failure mode is always the same: the paths in the list have to be relative to the rsync source argument. The breakdown, the dry run habit, and when rsync's own filters make find unnecessary.

Rank the biggest files on a full disk with find -printf '%s %p' piped to sort -rn. The GNU one-liner, the BSD stat variant for macOS, why -xdev matters, human-readable sizes, and when du or ncdu beats find.

How to Find the Largest Files on Disk (find, sort, du)

find / -xdev -type f -printf '%s %p\n' | sort -rn | head -20 gives you a ranked list of the biggest files on a full disk. The GNU one-liner, the BSD/macOS stat variant, why -xdev matters, human-readable output with numfmt, when to switch to du or ncdu for per-directory totals, and the mistakes that send a scan into /proc.