Skip to content
Go back

Recursively delete all empty subdirectories

· Updated:
By KingPin 6 min read
Recursively delete all empty subdirectories

Why your filesystem collects empty directories like lint traps

You move a bunch of files out of a deeply nested folder structure. Copy data to a new server. Reorganize a project. And suddenly you’ve got ghost directories, empty folders scattered throughout the tree like breadcrumbs nobody swept up.

They’re not hurting anything, but they clutter your backups, make navigating harder, and they’re the kind of tech debt that eventually someone (future you at 2 AM) will fix by hand. Recursively. Across 50 subdirectories. Fun times.

The good news: Unix gives you surgical tools to clean this up. The bad news: the tools are aggressive, so you need to understand what you’re doing first.

The classic one-liner: find + rmdir

Here’s the approach most people reach for:

Recursive directory deletion with find
find /home/user/somedir -depth -type d -empty -exec rmdir {} \;

Let me break this down:

The -depth flag matters. If you delete parent folders before children, the parent might not be empty anymore, and you’ll leave orphans behind. By processing depth-first, you clean bottom-up.

But first: the dry run

Listen. I’m gonna be direct: do a dry run first. Every time. No exceptions.

Preview what will be deleted
find /home/user/somedir -depth -type d -empty

Run that command first. Look at the output. Make sure those are actually the directories you want gone. This takes 30 seconds and saves your bacon.

If the output looks reasonable, then run the deletion command. One catch: the preview only lists directories that are empty right now. Delete them and their parents become empty too, so the real run removes nested chains (a/b/c and then a/b and a) that the preview showed as just a/b/c. Treat the preview as the minimum. Also, if the starting directory itself ends up empty, it gets removed too; add -mindepth 1 to protect it.

Using find’s built-in -delete flag

Modern versions of find have a -delete action that’s faster and safer than -exec rmdir:

Using find's native delete action
find /home/user/somedir -depth -type d -empty -delete

No rmdir needed. Same effect, one fewer process spawned. If your find supports it (it probably does), prefer this.

Still do the dry run first, just swap -delete for nothing.

What about hidden directories?

Hidden directories (ones starting with .) are included by default in find. If you want to exclude them, and everything inside them:

Skip hidden directories
find /home/user/somedir -depth -type d -empty -not -path '*/.*' -delete

The -not -path '*/.*' means “skip any path with a dot-named component.” This matters for .git: a repo keeps empty directories like .git/refs/tags, and deleting them can break the repo. A plain ! -name '.*' only skips directories that are themselves dot-named, so it would still empty out .git/refs/tags and then .git/refs. Note that -path tests the whole path, so keep the starting directory itself free of dot components.

Alternative: rmdir with --ignore-fail-on-non-empty

If you want less automation and more control, you can feed directories to rmdir directly:

Using rmdir with safety flag
find /home/user/somedir -depth -type d -empty -print0 | xargs -0 -r rmdir --ignore-fail-on-non-empty -v

Breaking it down:

This is slower than find -delete, and it has a catch: xargs deletes after find has already walked the tree, so parents of freshly emptied directories are not empty yet when find checks them. You remove only the deepest level per run. Repeat the command until it prints nothing, or use find -delete for a one-pass cleanup.

A preview script before committing

If you’re nervous (and you should be, at first), here’s a shell script that shows you exactly what will be deleted before asking for confirmation:

safe-rm-empty-dirs.sh
#!/bin/bash
DIRPATH="${1:-.}"
echo "=== Preview: empty directories to be deleted ==="
find "$DIRPATH" -depth -type d -empty | sort
echo ""
read -p "Delete these? (yes/no): " confirm
if [[ "$confirm" == "yes" ]]; then
find "$DIRPATH" -depth -type d -empty -delete
echo "Done!"
else
echo "Aborted."
fi

Run it: ./safe-rm-empty-dirs.sh /path/to/clean

It’s not fancy, but it forces you to look at what’s happening and explicitly confirm.

Edge cases and gotchas

Symlinks: By default find doesn’t follow symlinks. A symlink pointing at a directory matches -type l, not -type d, so it won’t get treated as a directory or deleted as one. The gotcha is if you pass -L (or the old -follow): then find dereferences links and a symlink-to-directory matches -type d. Adding -not -type l does not rescue you there, because under -L the -type l test only matches broken links. The fix is simple: never use -L or -follow with a delete.

Permission errors: If find encounters a directory you don’t have permission to read, it’ll skip it. The output might say “Permission denied” for some branches. That’s expected. You can silence those with 2>/dev/null if they’re noise:

Suppress permission errors
find /home/user/somedir -depth -type d -empty -delete 2>/dev/null

Race conditions: If another process is creating files in these directories while find runs, you might leave some behind. This is rare, but if you’re cleaning a live directory, that’s a risk.

The real lesson

Empty directories themselves aren’t evil. But they’re organizational debt. Clean them up once in a while, but do it carefully, think about what you’re deleting, preview first, and you’ll be fine.

Your future self will appreciate the tidy filesystem.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
SSH keys and secure file copy
Next Post
Directory FileCount

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts