rmdir Command in Linux: Complete Guide to Removing Empty Directories and Parameters

rmdir command in Linux and it perimeters

rmdir is the command I reach for specifically because of what it refuses to do — it will not remove a directory that still has something in it, full stop, no exceptions, no force flag that overrides that behavior. That built-in stubbornness is the entire point, and once you understand why it’s designed that way, it becomes a genuinely useful safety-conscious tool for cleanup scripts, distinct from the much more dangerous rm -rf. This guide covers rmdir completely, along with exactly where its limits are and why they exist.

What Is the rmdir Command?

rmdir removes one or more empty directories. If a directory contains any files, subdirectories, or even hidden dotfiles, rmdir refuses and reports an error — it will never delete directory contents, only the empty directory entry itself. It’s part of GNU coreutils and ships by default on every Linux distribution.

Basic Syntax

rmdir [OPTION]... DIRECTORY...

A Basic Example

$ mkdir -p emptytest/a/b/c
$ rmdir emptytest/a/b/c
$ ls emptytest/a/b

The deepest directory, c, was empty, so rmdir removed it cleanly, leaving emptytest/a/b (now itself empty, since c was its only content) intact.

Full Parameter Reference

OptionLong FormDescription
-p--parentsRemove the specified directory and then attempt to remove each parent directory in the given path, as long as each becomes empty in turn
--ignore-fail-on-non-emptySuppress the error (and don’t treat it as a failure) specifically when a directory can’t be removed because it’s non-empty
-v--verbosePrint a message for each directory processed
--helpDisplay usage information
--versionPrint version information

-p: Removing a Whole Empty Chain of Parent Directories

This is genuinely useful for cleaning up nested directory structures that were created for a single purpose and are now entirely empty top to bottom:

$ mkdir -p emptytest/a/b/c
$ rmdir -p emptytest/a/b
$ ls emptytest
ls: cannot access 'emptytest': No such file or directory

Here, rmdir -p emptytest/a/b removed b, then checked whether a was now empty (it was, since b was its only content) and removed it too, then checked emptytest itself and removed that as well — walking back up the path and removing each level as long as it keeps becoming empty. The moment -p encounters a parent that still has other content, it stops there and leaves that directory (and anything above it) alone.

Handling Non-Empty Directories Gracefully in Scripts

By default, trying to remove a non-empty directory produces an error and a non-zero exit code:

$ mkdir -p notempty
$ touch notempty/file.txt
$ rmdir notempty
rmdir: failed to remove 'notempty': Directory not empty
$ echo "exit:$?"
exit:1

--ignore-fail-on-non-empty is specifically designed for scripts that attempt cleanup across many directories and shouldn’t treat “this one still has stuff in it” as a fatal error:

$ rmdir --ignore-fail-on-non-empty notempty
$ echo "exit:$?"
exit:0

Notice the command still didn’t actually remove notempty (it’s still non-empty, so removal genuinely can’t happen), but it no longer reports failure via the exit code — this distinction matters a lot when rmdir is used inside a loop with set -e, where an unhandled non-zero exit would otherwise abort the whole script.

How rmdir Works Internally

rmdir is a thin wrapper around a single system call: rmdir(). This syscall performs an atomic check-and-remove operation at the kernel level — it verifies the target is a directory, verifies it contains no entries other than the implicit . and .., and if both conditions hold, unlinks the directory entry from its parent and frees the underlying inode (once no other references remain). If the directory isn’t empty, the kernel simply returns an error (ENOTEMPTY), and rmdir reports that back to you.

This is a meaningfully different mechanism from rm -r, which doesn’t rely on a single atomic “remove if empty” syscall at all — instead, rm -r recursively walks the directory tree, calling unlink() on every file it finds and rmdir() only on directories after they’ve been emptied of content, working from the bottom up. rmdir‘s refusal to touch non-empty directories isn’t a missing feature bolted on top of the same removal logic — it’s the actual, fundamental behavior of the underlying kernel syscall it’s built directly on.

This is exactly why rmdir can never be made to “force” delete a non-empty directory the way rm -rf can — there’s no equivalent flag, because doing so would require it to become an entirely different, recursive tool, which is precisely what rm -r/rm -rf already is.

Real-World Use Cases

1. Safe Cleanup After Verifying Emptiness

Because rmdir refuses non-empty directories, it’s inherently safer to use in automated cleanup than rm -rf when you specifically want to confirm nothing unexpected got left behind:

$ rmdir /tmp/build/staging

If this succeeds, you have a guarantee that staging genuinely had nothing left in it — no need for a separate verification step.

2. Cleaning Up Empty Directory Trees After a Batch Job

$ find /var/tmp/jobqueue -type d -empty -delete

While this uses find‘s own -delete action rather than rmdir directly, it’s worth mentioning here because find -type d -empty is doing conceptually the same safety check rmdir performs, and combining the two is common:

$ find /var/tmp/jobqueue -mindepth 1 -type d -exec rmdir --ignore-fail-on-non-empty {} \; 2>/dev/null

3. Removing a Chain of Now-Unused Nested Directories

$ rmdir -p /srv/app/releases/2025/01/15

If the release directory itself and each of its now-empty date-based parent directories should be cleaned up together, -p handles the whole chain in one command.

4. Validating a Directory Was Properly Emptied Before Proceeding

#!/bin/bash
if rmdir "$TARGET_DIR" 2>/dev/null; then
    echo "Confirmed empty and removed: $TARGET_DIR"
else
    echo "Directory not empty, investigate before proceeding" >&2
    exit 1
fi

Shell Scripting and Automation

A cleanup script that walks a tree of job-output directories and removes any that are now empty, tolerating the ones that aren’t, using --ignore-fail-on-non-empty so the script doesn’t need special-case error handling for the expected “still has files” case:

#!/bin/bash
# cleanup_empty_dirs.sh - remove empty subdirectories under a job output tree
set -euo pipefail

TARGET="/var/spool/myapp/jobs"

find "$TARGET" -mindepth 1 -type d | sort -r | while IFS= read -r dir; do
    if rmdir --ignore-fail-on-non-empty -v "$dir" 2>/dev/null; then
        :  # removed, verbose flag already printed a message
    fi
done

Sorting directory paths in reverse (sort -r) ensures deeper subdirectories are attempted before their parents, so a parent that becomes empty because its child was just removed still gets cleaned up in the same pass.

rmdir vs Related Commands

CommandPurpose
rmdirRemoves only empty directories; refuses anything with content, no override possible
rm -rRecursively removes a directory and all its contents
rm -rfSame as -r, but also suppresses confirmation prompts and ignores nonexistent-file errors — the most destructive common invocation
find -type d -empty -deleteBulk-removes every empty directory found in a tree, functionally similar to looping rmdir
rsync -a --delete (with an empty source)Sometimes used as an alternative bulk-deletion technique, though not directory-emptiness-aware in the same way

The core mental model worth keeping: rmdir is a verification-and-removal tool; rm -r/rm -rf is a destruction tool. If you’re not sure whether a directory should still have content, rmdir gives you a free correctness check as a side effect of attempting the removal — it simply can’t succeed unless the directory truly is empty.

Troubleshooting Common Issues

Problem: rmdir: failed to remove 'dir': Directory not empty even though ls shows nothing. Check for hidden dotfiles with ls -la — a directory containing only .hidden_file looks empty under a bare ls but is not empty from rmdir‘s perspective. Also check for a lingering lock file or a leftover .nfs* file from an NFS-mounted filesystem, which can appear even after you believe you’ve deleted everything.

Problem: rmdir -p stops partway through a chain of directories. This is expected behavior, not a bug — -p stops climbing upward the moment it hits a parent directory that isn’t empty (because something else lives alongside the chain you’re removing). Check what else exists in that parent with ls -la if you expected the removal to continue further up.

Problem: “Device or resource busy” when trying to remove a directory. Something still has the directory as its current working directory (a running process with its cwd set there), or it’s an active mount point. Check with lsof +D /path/to/dir or fuser -v /path/to/dir, and confirm it isn’t currently mounted with mount | grep /path/to/dir.

Problem: A script using rmdir in a loop aborts unexpectedly under set -e. rmdir‘s non-zero exit status on a non-empty directory will trigger set -e to abort the whole script unless handled. Use --ignore-fail-on-non-empty or explicit if/|| handling around the call, as shown in the examples above.

Performance Optimization

rmdir is a single, near-instant syscall per directory — there’s essentially no performance tuning relevant to the command itself. When removing large numbers of empty directories in bulk, the main consideration is avoiding unnecessary process-spawn overhead: prefer find -type d -empty -delete (which handles removal internally within a single find invocation) over a shell loop spawning a fresh rmdir process for every single directory, if you’re operating across a very large tree.

Security Implications

rmdir‘s inherent refusal to touch non-empty directories makes it meaningfully lower-risk than rm -r/rm -rf in automated scripts — there’s no way to accidentally cause cascading data loss through a rmdir call the way a mistyped path in an rm -rf invocation can. That said, standard path-handling caution still applies: always quote directory variables (rmdir "$dir", not rmdir $dir) to avoid word-splitting or glob-expansion surprises, and be cautious with -p on paths built from untrusted input, since it will walk upward removing every empty parent in the chain — in an unusual scenario where an attacker could influence both the path and ensure intermediate directories are empty, this could in principle be used to remove more directory structure than intended, though the impact is limited strictly to directory structure, never to file content, since rmdir can never remove anything containing data.

Compatibility Across Distributions

rmdir is part of GNU coreutils and behaves identically across Ubuntu, Debian, Fedora, RHEL, Arch, and openSUSE. The core behavior (refusing non-empty directories) is actually defined by the POSIX standard and the underlying rmdir() syscall semantics shared across virtually all Unix-like systems, including macOS and BSD — so this is one of the most portable and consistently-behaved commands across platforms. Flag-level differences are minimal; -p and --ignore-fail-on-non-empty are both POSIX/GNU-standard and widely available, though very old or minimal (BusyBox) implementations may lack the long-form --ignore-fail-on-non-empty spelling — check rmdir --help in constrained environments like Alpine-based containers.

Best Practices

  • Prefer rmdir over rm -r/rm -rf whenever you specifically want the built-in “only if actually empty” safety guarantee, rather than unconditional recursive deletion.
  • Use --ignore-fail-on-non-empty in scripts that sweep across many directories and shouldn’t abort just because some of them still have content.
  • Use -p deliberately, understanding it will climb and remove every empty parent in the chain, not just the named directory.
  • Always quote directory path variables in scripts to avoid word-splitting issues.
  • When rmdir unexpectedly fails on a directory that looks empty, check for hidden dotfiles and lingering .nfs*/lock files before assuming something is wrong with the command itself.

Summary

rmdir is a deliberately narrow, safety-first tool: it removes empty directories and nothing else, with no override to force it into destructive territory — that boundary is enforced directly by the underlying kernel syscall, not a removable safety flag. That narrowness is exactly what makes it valuable in cleanup scripts and automated pipelines where you want a guarantee that nothing with actual content gets silently swept away. For anything beyond empty-directory removal, rm -r/rm -rf is the appropriate (and appropriately more dangerous) tool.

References

  • GNU Coreutils Manual: rmdir — https://www.gnu.org/software/coreutils/manual/html_node/rmdir-invocation.html
  • Linux man-pages project: man 1 rmdir and man 2 rmdir — https://man7.org/linux/man-pages/man2/rmdir.2.html
  • POSIX Specification: rmdir — https://pubs.opengroup.org/onlinepubs/9699919799/utilities/rmdir.html
  • Ubuntu Manpage Repository — https://manpages.ubuntu.com/manpages/noble/en/man1/rmdir.1.html
Total
0
Shares

Leave a Reply

Previous Post
rm command in Linux and it perimeters

rm Command in Linux: Complete Guide to Removing Files, Directories, and Parameters

Next Post
pwd command in Linux and it perimeters

pwd Command in Linux: Complete Guide to Print Working Directory and Parameters

Related Posts