Command documentation sourced from the linux-command project This comprehensive command reference is part of the linux-command documentation project.
rmdir - Remove Empty Directories
The rmdir command is a specialized Unix/Linux utility designed specifically for removing empty directories from the filesystem. Unlike rm -r, which can recursively remove directories and their contents, rmdir provides a safety mechanism by only deleting directories that contain no files or subdirectories. This makes it an ideal tool for cleanup operations, automated scripts, and system maintenance tasks where you want to ensure that no data is accidentally lost. The command is part of the GNU Core Utilities package and is available on virtually all Linux and Unix-like systems.
Basic Syntax
rmdir [OPTIONS] DIRECTORY...
Common Options
Directory Removal Options
-p, --parents- Remove DIRECTORY and its ancestor directories if they become empty-v, --verbose- Output a diagnostic message for every directory processed--ignore-fail-on-non-empty- Ignore each failure that is solely because a directory is non-empty
Information Options
--help- Display help information and exit--version- Output version information and exit
Usage Examples
Basic Directory Removal
Single Directory Operations
# Remove a single empty directory
rmdir empty_directory
# Remove multiple empty directories
rmdir temp_dir cache_dir logs_dir
# Remove with verbose output to see what's happening
rmdir -v empty_directory
# rmdir: removing directory, 'empty_directory'
# Dry run - see what would be removed without actually removing
rmdir --verbose --ignore-fail-on-non-empty possibly_empty_dirs/*
Multiple Directory Operations
# Remove several empty directories at once
rmdir dir1 dir2 dir3
# Remove empty directories using glob patterns
rmdir /tmp/temp_*
# Remove directories with spaces in names
rmdir "empty directory with spaces"
Parent Directory Removal
Recursive Empty Path Removal
# Remove directory and its empty parents
rmdir -p path/to/empty/directory
# Example: removes directory, then empty, then to, then path
# if they're all empty after each removal
mkdir -p a/b/c/d
rmdir -p a/b/c/d
# All directories a, b, c, d will be removed if they're empty
# Remove multiple nested paths
rmdir -p project/build/output/classes
rmdir -p cache/session/user/temp
Practical Parent Removal Examples
# Remove entire empty directory structure created during build
rmdir -p build/output/classes/com/example/app
# Clean up empty cache directories
rmdir -p cache/user/123/session/data
# Remove empty temporary directory trees
rmdir -p /tmp/backup_20231201/daily/database
Safe Operations with Error Handling
Handling Non-Empty Directories
# Remove empty directories only (default behavior)
rmdir empty_dir
# Ignore non-empty directories and continue processing others
rmdir --ignore-fail-on-non-empty dir1 dir2 dir3
# Combine with verbose output for detailed logging
rmdir -v --ignore-fail-on-non-empty temp_dirs/*
Error Prevention Strategies
# Check if directory is empty before attempting removal
if [ -z "$(ls -A some_directory)" ]; then
rmdir some_directory
echo "Successfully removed empty directory"
else
echo "Directory is not empty, skipping"
fi
# Safe batch removal with error handling
for dir in dir1 dir2 dir3; do
if [ -d "$dir" ] && [ -z "$(ls -A "$dir")" ]; then
rmdir -v "$dir"
else
echo "Skipping $dir - not empty or doesn't exist"
fi
done
Advanced Usage
Combining with Other Commands
Integration with find Command
# Find and remove all empty directories in current tree
find . -type d -empty -exec rmdir {} \;
# Find and remove empty directories with verbose output
find . -type d -empty -exec rmdir -v {} \;
# Remove empty directories deeper than 2 levels
find . -mindepth 3 -type d -empty -exec rmdir {} \;
# Remove empty directories modified more than 7 days ago
find . -type d -empty -mtime +7 -exec rmdir {} \;
# Use find with xargs for better performance with many directories
find . -type d -empty -print0 | xargs -0 rmdir -v
Pipeline Operations
# Remove empty directories listed in a file
cat empty_dirs.txt | xargs rmdir -v
# Remove empty directories from command output
ls -d */ | grep "^empty" | xargs rmdir
# Process directory names from find with additional filtering
find . -type d -empty | grep -v "^./.git" | xargs rmdir -v
Complex Script Applications
Automated Cleanup Script
#!/bin/bash
# Comprehensive empty directory cleanup script
CLEANUP_DIRS="/tmp /var/log /home/user/downloads"
MAX_DEPTH=3
LOG_FILE="/var/log/cleanup.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
cleanup_empty_dirs() {
local base_dir="$1"
log "Starting cleanup in $base_dir"
# Find empty directories within depth limit
find "$base_dir" -maxdepth $MAX_DEPTH -type d -empty -print0 | while IFS= read -r -d '' dir; do
log "Removing empty directory: $dir"
if rmdir -v "$dir" 2>/dev/null; then
log "Successfully removed: $dir"
else
log "Failed to remove: $dir"
fi
done
# Remove empty parent directories
find "$base_dir" -maxdepth $MAX_DEPTH -type d -empty -exec rmdir -p {} \; 2>/dev/null
}
# Process each directory
for dir in $CLEANUP_DIRS; do
if [ -d "$dir" ]; then
cleanup_empty_dirs "$dir"
else
log "Directory does not exist: $dir"
fi
done
log "Cleanup completed"
Build System Integration
#!/bin/bash
# Post-build cleanup for development projects
PROJECT_ROOT="$(pwd)"
BUILD_DIRS=("build" "target" "out" "dist" ".gradle" "node_modules/.cache")
clean_build_artifacts() {
local dir="$1"
# Remove empty directories in build paths
if [ -d "$dir" ]; then
log "Cleaning $dir"
# Remove empty directories first
find "$dir" -type d -empty -exec rmdir -v {} \; 2>/dev/null
# Remove empty parent directories
find "$dir" -type d -empty -exec rmdir -p {} \; 2>/dev/null
# If the main directory is empty, remove it too
if [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then
rmdir -v "$dir"
fi
fi
}
# Clean all build directories
for build_dir in "${BUILD_DIRS[@]}"; do
clean_build_artifacts "$PROJECT_ROOT/$build_dir"
done
Directory Structure Maintenance
#!/bin/bash
# Maintain directory structure by removing empty directories
MAINTAIN_DIR="/data/structured_storage"
PATTERNS=("temp_*" "cache_*" "backup_*")
maintain_structure() {
local base_dir="$1"
for pattern in "${PATTERNS[@]}"; do
# Remove empty directories matching pattern
find "$base_dir" -maxdepth 1 -type d -name "$pattern" -empty -exec rmdir -v {} \;
# Remove empty subdirectories within pattern directories
find "$base_dir" -type d -name "$pattern" -exec find {} -type d -empty -exec rmdir -v {} \; \;
done
# Clean up any remaining empty directories
find "$base_dir" -mindepth 2 -type d -empty -exec rmdir -p {} \; 2>/dev/null
}
if [ -d "$MAINTAIN_DIR" ]; then
maintain_structure "$MAINTAIN_DIR"
else
echo "Maintenance directory does not exist: $MAINTAIN_DIR"
fi
System Administration Applications
Log Directory Management
Automated Log Cleanup
#!/bin/bash
# Advanced log directory cleanup with rmdir
LOG_BASE="/var/log"
RETENTION_DAYS=30
EMPTY_LOG_DIRS=()
# Function to check and remove empty log directories
cleanup_log_dirs() {
local log_type="$1"
local log_dir="$LOG_BASE/$log_type"
if [ -d "$log_dir" ]; then
# Find empty directories in log structure
while IFS= read -r -d '' empty_dir; do
EMPTY_LOG_DIRS+=("$empty_dir")
done < <(find "$log_dir" -type d -empty -print0)
# Remove empty directories with parent cleanup
find "$log_dir" -type d -empty -exec rmdir -v -p {} \; 2>/dev/null
fi
}
# Process different log types
for log_type in app system security archive; do
cleanup_log_dirs "$log_type"
done
# Report cleanup results
if [ ${#EMPTY_LOG_DIRS[@]} -gt 0 ]; then
echo "Cleaned up ${#EMPTY_LOG_DIRS[@]} empty log directories:"
printf '%s\n' "${EMPTY_LOG_DIRS[@]}"
fi
Temporary Directory Management
# Remove empty temporary directories by age
find /tmp -type d -empty -mtime +1 -exec rmdir -v {} \;
# Clean up empty user temp directories
find /home -name "tmp" -type d -empty -exec rmdir -v {} \;
# Remove empty cache directories
find /var/cache -type d -empty -exec rmdir -p {} \;
Application-Specific Cleanup
Web Application Cleanup
#!/bin/bash
# Web application session and cache cleanup
WEB_ROOT="/var/www/html"
SESSION_TIMEOUT=3600 # 1 hour in seconds
CURRENT_TIME=$(date +%s)
# Clean up empty session directories
find "$WEB_ROOT/sessions" -type d -empty -exec rmdir -v {} \;
# Remove empty cache directories
for cache_dir in "$WEB_ROOT/cache" "$WEB_ROOT/temp" "$WEB_ROOT/uploads/tmp"; do
if [ -d "$cache_dir" ]; then
# Remove empty directories within cache
find "$cache_dir" -mindepth 1 -type d -empty -exec rmdir -v {} \;
# Remove empty parent directories
find "$cache_dir" -type d -empty -exec rmdir -p {} \; 2>/dev/null
fi
done
# Clean up empty upload directories by user
find "$WEB_ROOT/uploads" -mindepth 2 -maxdepth 2 -type d -empty -exec rmdir -v {} \;
Database Backup Cleanup
#!/bin/bash
# Database backup directory maintenance
BACKUP_BASE="/backup/database"
KEEP_DAYS=7
# Function to clean empty backup directories
cleanup_backup_structure() {
local db_type="$1"
local backup_dir="$BACKUP_BASE/$db_type"
if [ -d "$backup_dir" ]; then
echo "Cleaning $db_type backup directories..."
# Remove empty date directories
find "$backup_dir" -mindepth 1 -maxdepth 1 -type d -empty -exec rmdir -v {} \;
# Remove empty database directories
find "$backup_dir" -mindepth 2 -maxdepth 2 -type d -empty -exec rmdir -v {} \;
# Remove empty parent directories
find "$backup_dir" -type d -empty -exec rmdir -p {} \; 2>/dev/null
fi
}
# Clean different database types
for db_type in mysql postgresql mongodb; do
cleanup_backup_structure "$db_type"
done
Performance Optimization
Large-Scale Operations
Efficient Bulk Removal
# Use xargs for handling many directories efficiently
find /path -type d -empty -print0 | xargs -0 -P 4 rmdir -v
# Parallel processing with GNU parallel
find . -type d -empty | parallel -j 4 rmdir -v {}
# Batch processing for very large directory trees
find /large/dataset -type d -empty -print0 | split -d -l 1000 - /tmp/batch_
for batch in /tmp/batch_*; do
xargs -0 -a "$batch" rmdir -v
rm "$batch"
done
Memory-Efficient Operations
# Process directories in chunks to avoid memory issues
find /huge/path -type d -empty | while read -r dir; do
rmdir -v "$dir"
# Optional: add delay to reduce system load
sleep 0.01
done
# Use nice to reduce process priority
nice -n 19 find /path -type d -empty -exec rmdir {} \;
Monitoring and Progress Tracking
Progress Reporting
#!/bin/bash
# rmdir operation with progress tracking
TOTAL_DIRS=$(find . -type d -empty | wc -l)
CURRENT=0
find . -type d -empty -print0 | while IFS= read -r -d '' dir; do
CURRENT=$((CURRENT + 1))
PERCENT=$((CURRENT * 100 / TOTAL_DIRS))
printf "\rProgress: %d/%d (%d%%) - %s" "$CURRENT" "$TOTAL_DIRS" "$PERCENT" "$dir"
if rmdir "$dir" 2>/dev/null; then
printf " ✓"
else
printf " ✗"
fi
done
echo # New line after progress
Detailed Logging
#!/bin/bash
# rmdir with comprehensive logging
LOG_FILE="/tmp/rmdir_operation_$(date +%Y%m%d_%H%M%S).log"
SUCCESS_COUNT=0
FAIL_COUNT=0
log_operation() {
local dir="$1"
local result="$2"
if [ "$result" -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') SUCCESS: $dir" >> "$LOG_FILE"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo "$(date '+%Y-%m-%d %H:%M:%S') FAILED: $dir" >> "$LOG_FILE"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
}
# Process directories with detailed logging
find /path -type d -empty -print0 | while IFS= read -r -d '' dir; do
rmdir "$dir" 2>/dev/null
log_operation "$dir" $?
done
echo "Operation completed. Check $LOG_FILE for details."
echo "Success: $SUCCESS_COUNT, Failed: $FAIL_COUNT"
Error Handling and Troubleshooting
Common Error Scenarios
Permission Issues
# Handle permission denied errors
find /path -type d -empty -exec rmdir {} \; 2>&1 | grep "Permission denied"
# Use sudo for system directories
sudo find /var/log -type d -empty -exec rmdir {} \;
# Check permissions before attempting removal
find /path -type d -empty -exec test -w {} \; -exec rmdir -v {} \;
Non-Empty Directory Handling
# Skip non-empty directories gracefully
rmdir --ignore-fail-on-non-empty dir1 dir2 dir3
# Identify which directories are non-empty
for dir in dir1 dir2 dir3; do
if [ -n "$(ls -A "$dir" 2>/dev/null)" ]; then
echo "$dir is not empty"
else
rmdir "$dir"
fi
done
Race Conditions
# Handle race conditions in multi-process environments
if mkdir /tmp/rmdir_lock 2>/dev/null; then
# Critical section
find /path -type d -empty -exec rmdir {} \;
rmdir /tmp/rmdir_lock
else
echo "Another rmdir operation is in progress"
exit 1
fi
Advanced Debugging
Troubleshooting Script
#!/bin/bash
# Comprehensive rmdir troubleshooting script
TARGET_DIR="$1"
DEBUG_FILE="/tmp/rmdir_debug_$(date +%Y%m%d_%H%M%S).log"
debug_rmdir() {
local dir="$1"
{
echo "=== Debugging directory: $dir ==="
echo "Directory exists: $([ -d "$dir" ] && echo "YES" || echo "NO")"
echo "Directory is empty: $([ -z "$(ls -A "$dir" 2>/dev/null)" ] && echo "YES" || echo "NO")"
echo "Directory is writable: $([ -w "$dir" ] && echo "YES" || echo "NO")"
echo "Directory contents:"
ls -la "$dir" 2>/dev/null || echo "Cannot list directory"
echo "Attempting rmdir..."
if rmdir -v "$dir" 2>&1; then
echo "SUCCESS: Directory removed"
else
echo "FAILED: Directory removal failed"
echo "Exit code: $?"
fi
echo ""
} >> "$DEBUG_FILE"
}
if [ -z "$TARGET_DIR" ]; then
echo "Usage: $0 <directory>"
exit 1
fi
if [ -d "$TARGET_DIR" ]; then
debug_rmdir "$TARGET_DIR"
echo "Debug information saved to: $DEBUG_FILE"
else
echo "Directory does not exist: $TARGET_DIR"
fi
Integration with Other Tools
Shell Integration
Function for Safe Directory Removal
# Add to .bashrc or .zshrc
safe_rmdir() {
local dir="$1"
local verbose="$2"
if [ -z "$dir" ]; then
echo "Usage: safe_rmdir <directory> [-v]"
return 1
fi
if [ ! -d "$dir" ]; then
echo "Error: Directory '$dir' does not exist"
return 1
fi
if [ -n "$(ls -A "$dir" 2>/dev/null)" ]; then
echo "Error: Directory '$dir' is not empty"
return 1
fi
if [ "$verbose" = "-v" ]; then
rmdir -v "$dir"
else
rmdir "$dir"
fi
}
# Usage examples:
# safe_rmdir empty_dir
# safe_rmdir empty_dir -v
Completion Function
# Bash completion for rmdir
_rmdir_completion() {
local cur prev dirs
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == -* ]]; then
COMPREPLY=( $(compgen -W "--verbose --parents --ignore-fail-on-non-empty --help --version" -- "$cur") )
else
# Complete with empty directories only
dirs=$(find . -maxdepth 2 -type d -empty 2>/dev/null | sed 's|^\./||')
COMPREPLY=( $(compgen -W "$dirs" -- "$cur") )
fi
}
complete -F _rmdir_completion rmdir
Automation Framework Integration
Cron Job for Regular Cleanup
# Add to crontab with: crontab -e
# Run daily at 2 AM to clean empty directories
0 2 * * * /usr/local/bin/cleanup_empty_dirs.sh
# cleanup_empty_dirs.sh
#!/bin/bash
LOG_FILE="/var/log/daily_cleanup.log"
{
echo "$(date): Starting daily empty directory cleanup"
# System directories
find /tmp -type d -empty -mtime +1 -exec rmdir -v {} \; 2>/dev/null
find /var/tmp -type d -empty -mtime +1 -exec rmdir -v {} \; 2>/dev/null
# User directories
find /home -maxdepth 3 -type d -name ".tmp" -empty -exec rmdir -v {} \; 2>/dev/null
# Application directories
find /var/log -type d -empty -mtime +7 -exec rmdir -v {} \; 2>/dev/null
echo "$(date): Daily cleanup completed"
} >> "$LOG_FILE" 2>&1
Systemd Service Integration
# /etc/systemd/system/cleanup-empty-dirs.service
[Unit]
Description=Cleanup Empty Directories
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup_empty_dirs.sh
User=root
Group=root
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/cleanup-empty-dirs.timer
[Unit]
Description=Run empty directory cleanup daily
Requires=cleanup-empty-dirs.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
Security Considerations
Safe Removal Practices
Verification Before Removal
#!/bin/bash
# Safe rmdir with verification
verify_and_remove() {
local dir="$1"
local interactive="$2"
# Verify directory exists
if [ ! -d "$dir" ]; then
echo "Directory does not exist: $dir"
return 1
fi
# Verify directory is empty
if [ -n "$(ls -A "$dir" 2>/dev/null)" ]; then
echo "Directory is not empty: $dir"
echo "Contents:"
ls -la "$dir"
return 1
fi
# Interactive confirmation if requested
if [ "$interactive" = "true" ]; then
echo "Directory '$dir' is empty. Remove? (y/N)"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
echo "Skipping: $dir"
return 0
fi
fi
# Perform removal
if rmdir -v "$dir"; then
echo "Successfully removed: $dir"
return 0
else
echo "Failed to remove: $dir"
return 1
fi
}
# Usage examples:
# verify_and_remove "/tmp/empty_dir" "false"
# verify_and_remove "/tmp/empty_dir" "true"
Auditing and Logging
#!/bin/bash
# rmdir with comprehensive auditing
AUDIT_LOG="/var/log/rmdir_audit.log"
OPERATOR=$(whoami)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
audit_rmdir() {
local dir="$1"
local result="$2"
audit_entry="$TIMESTAMP - $OPERATOR - rmdir - $dir - $([ "$result" -eq 0 ] && echo "SUCCESS" || echo "FAILED")"
echo "$audit_entry" >> "$AUDIT_LOG"
# Also log to syslog for centralized logging
logger -t "rmdir_audit" "$audit_entry"
}
# Wrapper function for audited rmdir operations
audited_rmdir() {
local dir="$1"
local options="$2"
if rmdir $options "$dir" 2>/dev/null; then
audit_rmdir "$dir" 0
return 0
else
audit_rmdir "$dir" 1
return 1
fi
}
Related Commands
rm- Remove files and directories (recursive)mkdir- Create directoriesfind- Find files and directoriesls- List directory contentstree- Display directory structurepwd- Print working directorycd- Change directory
Best Practices
- Use rmdir when you want to ensure directories are empty before removal
- Combine with find for efficient bulk empty directory cleanup
- Use verbose mode (
-v) for monitoring and debugging removal operations - Check directory contents before removal in critical scripts
- Use
--ignore-fail-on-non-emptyfor batch operations where non-empty directories should be skipped - Employ parent removal (
-p) for cleaning up entire empty directory trees - Test operations with verbose output before running in production
- Log removals for audit trails and troubleshooting
- Use absolute paths in scripts to avoid ambiguity
- Handle permissions appropriately for system directory operations
Performance Tips
- Use find with -empty for efficient location of empty directories
- Employ xargs with -print0 for handling directories with special characters
- Consider parallel processing with GNU parallel for large-scale operations
- Use nice to reduce priority for long-running cleanup operations
- Batch operations to reduce system call overhead
- Avoid recursive solutions when find + rmdir is more efficient
- Monitor disk I/O during large-scale directory removal operations
- Use appropriate buffer sizes when processing directory lists
- Consider filesystem-specific optimizations for different storage types
- Plan operations during low-usage periods for production systems
Advanced Patterns
Conditional Directory Removal
# Remove directories only if certain conditions are met
find /data -type d -empty -mtime +30 -size 0 -exec rmdir -v {} \;
# Remove empty directories based on naming patterns
find /tmp -type d -empty -name "temp_*" -exec rmdir -v {} \;
# Remove empty directories but preserve certain structures
find /project -type d -empty ! -path '*/.git/*' ! -path '*/node_modules/*' -exec rmdir -v {} \;
State-Aware Directory Management
#!/bin/bash
# Directory management with state tracking
STATE_FILE="/tmp/dir_cleanup_state.json"
# Function to track directory state
track_directory_state() {
local action="$1"
local dir="$2"
local timestamp=$(date -Iseconds)
# Update state file (simplified JSON)
echo "{\"timestamp\":\"$timestamp\",\"action\":\"$action\",\"directory\":\"$dir\"}" >> "$STATE_FILE"
}
# Function to check if directory was recently modified
is_recently_modified() {
local dir="$1"
local hours="${2:-24}"
local cutoff_time=$(($(date +%s) - hours * 3600))
local dir_time=$(stat -c %Y "$dir" 2>/dev/null)
[ -n "$dir_time" ] && [ "$dir_time" -gt "$cutoff_time" ]
}
# Safe removal with state tracking
safe_remove_with_state() {
local dir="$1"
if [ -d "$dir" ] && [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then
if ! is_recently_modified "$dir" 48; then
if rmdir -v "$dir"; then
track_directory_state "removed" "$dir"
return 0
fi
else
echo "Skipping recently modified empty directory: $dir"
track_directory_state "skipped_recent" "$dir"
fi
fi
return 1
}
The rmdir command provides a safe and efficient way to remove empty directories while maintaining data integrity. Its focused functionality makes it ideal for automated cleanup operations, build system maintenance, and system administration tasks where preventing accidental data loss is crucial. When combined with other Unix tools and proper scripting practices, rmdir becomes a powerful component in comprehensive directory management strategies.