How to Delete Files Matching a Pattern in Bash
Quick Answer: Delete Files Matching a Pattern
To delete files matching a pattern in Bash, use rm with wildcards: rm *.log. For more control, use the find command: find . -name "*.log" -delete. Always test patterns with ls first to avoid accidental deletion.
Quick Comparison: Pattern-Based Deletion Methods
| Method | Pattern Type | Safety | Best For |
|---|---|---|---|
| *rm .pattern | Wildcard | Test first | Simple patterns |
| find -delete | Complex regex | Dry-run option | Complex patterns |
| find -exec rm | Complex regex | Safe | Maximum control |
| rm -i | Any pattern | Interactive | Critical files |
| rm -f | Any pattern | Dangerous | Forced deletion |
Bottom line: Use find for complex patterns, always test first.
Method 1: Using Wildcards (Simplest, but Limited)
Wildcards are the quickest way to delete files matching a simple pattern in the current directory. The shell expands them before rm sees them, so you’re really running rm with a list of specific filenames.
Basic Wildcard Deletion
Here’s the fundamental pattern:
rm *.log
This deletes all files ending with .log in the current directory. The * matches any characters, so *.log matches error.log, debug.log, app.log, etc.
Wildcard Pattern Examples
# Delete all files starting with "temp"
rm temp*
# Delete all files ending with ".bak"
rm *.bak
# Delete files matching "test" anywhere
rm *test*
Each pattern expands to match zero or more files, which are then deleted.
Critical Gotcha: What Happens with No Matches?
Here’s where wildcards can surprise you:
rm *.xyz # If no .xyz files exist, this returns an error
# (and doesn't delete anything)
But with shopt -s nullglob:
shopt -s nullglob
rm *.xyz # If no .xyz files exist, the pattern becomes empty
# (and rm is never called—no error)
This matters in scripts. If the pattern matches nothing, rm might be called with zero arguments, which can cause unexpected behavior.
When to Use Wildcard Deletion
Use wildcards when:
- Pattern is simple (just extensions like
*.log) - You’re only working in the current directory
- You’re confident about what matches
- You’ve already tested with
lsfirst - It’s a one-off command
Avoid wildcards when:
- Searching subdirectories (use
findinstead) - Pattern is complex (use
findinstead) - Files might have special characters in names
- Safety is critical (use
find -deletewith--dry-runinstead)
Method 2: Using find Command (Powerful and Safe)
The find command is the professional tool for deleting files matching complex patterns. It searches recursively through subdirectories and offers many filtering options.
Basic Pattern Deletion with find
find . -name "*.log" -delete
This finds all files matching *.log in the current directory and subdirectories, then deletes them. The -delete action removes each matched file.
Find All Matching Files Without Deleting
Before actually deleting, always verify what matches:
find . -name "*.log"
This lists every file that would be deleted. Review the output carefully to ensure it matches your intent. Only after confirming run the actual delete command.
Find Files by Multiple Criteria
Find lets you combine conditions to be very specific:
# Delete .tmp files older than 7 days
find . -name "*.tmp" -mtime +7 -delete
# Delete empty files
find . -type f -empty -delete
# Delete .log files modified in the last day
find . -name "*.log" -mtime -1 -delete
Each condition refines what gets matched. The combination must be true for the file to be deleted.
When to Use find -delete
Use find when:
- Searching subdirectories (recursively)
- Deleting based on time (modification/access date)
- Combining multiple conditions
- File size matters
- Complex patterns needed
Avoid it when:
- Simple wildcard pattern works fine
- You only need current directory
Method 3: Using find with -exec (Maximum Control)
For situations where you want to see exactly what’s being deleted, use -exec rm instead of -delete. This gives you more control and visibility.
Delete with explicit rm command
find . -name "*.log" -exec rm {} \;
The {} is replaced with each matched filename. The \; ends the command.
Delete with Verbose Output
find . -name "*.log" -exec rm -v {} \;
The -v flag makes rm print what it’s deleting. Now you see exactly which files are removed.
Delete with Confirmation
find . -name "*.log" -exec rm -i {} \;
The -i flag makes rm ask for confirmation before deleting each file. Slow but safe for critical files.
The Problem with -exec rm vs -delete
The -exec approach is slower because it spawns a new rm process for each file. With many files, this becomes noticeable. Use -delete for performance when you don’t need -i or -v.
When to Use find -exec
Use -exec when:
- You want to see verbose output while deleting
- You need confirmation for each file (
-i) - You’re unsure and want to go slow
- You want the combined safety of find with visibility of rm
Use -delete when:
- You’re confident about the pattern
- Speed matters (many files)
- You’ve already verified with
ls
Method 4: Using a Loop with Confirmation
For maximum control and safety, iterate with a loop and prompt before each deletion.
Delete with Per-File Confirmation
for file in *.log; do
if [ -f "$file" ]; then
read -p "Delete $file? (y/n) " confirm
if [ "$confirm" = "y" ]; then
rm "$file"
echo "Deleted: $file"
fi
fi
done
Each file gets checked individually, and you confirm before deletion. The [ -f "$file" ] check ensures it’s a regular file (not a directory or symlink).
Simpler Version with rm -i
for file in *.log; do
[ -f "$file" ] && rm -i "$file"
done
This is shorter. The && means “only run rm if the file exists.” The -i flag makes rm ask before each deletion.
When to Use Loop Deletion
Use loops when:
- You want interactive confirmation
- Script needs to report what it’s deleting
- Selective deletion (delete some but not all matches)
- You need to do other operations on matched files
Avoid loops when:
- Many files exist (too slow)
- Just simple one-time deletion
Practical Examples
Example 1: Delete Old Log Files Safely
#!/bin/bash
# Step 1: Find and list files older than 30 days
echo "Log files older than 30 days:"
find /var/log -name "*.log" -mtime +30 -type f
# Step 2: Get confirmation
read -p "Delete these files? (y/n) " confirm
# Step 3: Delete if confirmed
if [ "$confirm" = "y" ]; then
find /var/log -name "*.log" -mtime +30 -type f -delete
echo "Cleanup complete"
else
echo "Cancelled"
fi
This follows the three-step safe deletion pattern: list, confirm, then delete.
Example 2: Delete Multiple Extensions
#!/bin/bash
# Delete backup and temporary files
echo "Deleting backup and temp files..."
find . -type f \( -name "*.bak" -o -name "*.tmp" -o -name "*.swp" \) -delete
echo "Cleanup complete"
The \( ... \) groups conditions with -o (OR). This deletes files matching ANY of the extensions.
Example 3: Delete Temporary Files Older Than a Week
#!/bin/bash
# Clean up /tmp directory
tmp_dir="/tmp"
days_old=7
echo "Deleting files in $tmp_dir older than $days_old days..."
# List files first
find "$tmp_dir" -type f -mtime "+$days_old" | head -20
# Confirm before deleting
read -p "Proceed? (y/n) " -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
find "$tmp_dir" -type f -mtime "+$days_old" -delete
echo "Deletion complete"
fi
Useful for maintenance scripts that clean up old temporary files.
Example 4: Delete Empty Files Only
#!/bin/bash
# Find and remove empty files
echo "Empty files to be deleted:"
find . -type f -empty
read -p "Delete empty files? (y/n) " confirm
if [ "$confirm" = "y" ]; then
find . -type f -empty -delete
echo "Empty files deleted"
fi
The -empty flag matches only zero-length files. Safe because you only target files with no content.
Example 5: Delete with Logging
#!/bin/bash
log_file="/var/log/cleanup.log"
echo "Cleanup started at $(date)" >> "$log_file"
# Delete and log
find . -name "*.tmp" -delete -printf "Deleted: %p\n" >> "$log_file"
echo "Cleanup completed" >> "$log_file"
# Show what was deleted
cat "$log_file"
The -printf action (instead of -delete alone) logs each deletion.
Example 6: Delete with Size Limit
#!/bin/bash
# Delete large log files (over 100MB)
echo "Large log files to be deleted:"
find /var/log -name "*.log" -size +100M
read -p "Delete large files? (y/n) " confirm
if [ "$confirm" = "y" ]; then
find /var/log -name "*.log" -size +100M -delete
echo "Deletion complete"
fi
The -size condition filters by file size. +100M means “greater than 100MB”.
Example 7: Dry Run Pattern (Test Before Real Deletion)
#!/bin/bash
# This is the pattern: test BEFORE actual deletion
pattern="*.log"
location="."
# Step 1: DRY RUN - see what would be deleted
echo "=== DRY RUN: Files that would be deleted ==="
find "$location" -name "$pattern" -type f
match_count=$(find "$location" -name "$pattern" -type f | wc -l)
echo ""
echo "Total files to delete: $match_count"
# Step 2: Confirm
read -p "Proceed with deletion? (y/n) " confirm
# Step 3: Real deletion
if [ "$confirm" = "y" ]; then
echo "Deleting..."
find "$location" -name "$pattern" -type f -delete
echo "Deletion complete"
else
echo "Cancelled - no files deleted"
fi
This pattern is highly recommended: show what would be deleted, count matches, confirm, then delete.
Safety Best Practices
Always Test First
# STEP 1: See what matches
find . -name "*.log"
# STEP 2: Only then delete
find . -name "*.log" -delete
This two-step approach prevents accidents. If the first command matches something unexpected, you catch it before deletion.
Use -type f to Match Only Files
# Match files only, not directories
find . -type f -name "*.log" -delete
# WITHOUT -type f could match directories too
find . -name "*.log" -delete # Riskier!
The -type f restricts to regular files. Without it, a directory matching the pattern would be an error, but it’s safer to be explicit.
Consider Using find with -print Before -delete
# See files as they're being deleted
find . -name "*.log" -print -delete
The -print action outputs the filename before it’s deleted, giving you a log of what happened.
Be Careful with Wildcards in Scripts
# This can be dangerous in scripts
rm *.log # If no .log files, might cause issues
# Better: use find
find . -name "*.log" -delete # Safe—handles zero matches
Quick Reference
# List files matching pattern first (always do this!)
find . -name "*.log"
# Delete simple pattern in current directory
rm *.log
# Delete with find (recursive, safe)
find . -name "*.log" -delete
# Delete with confirmation
find . -name "*.log" -exec rm -i {} \;
# Delete old files (older than 30 days)
find . -name "*.log" -mtime +30 -delete
# Delete empty files
find . -type f -empty -delete
# Delete files matching multiple patterns
find . \( -name "*.log" -o -name "*.tmp" \) -delete
# Delete files larger than 100MB
find . -name "*.log" -size +100M -delete
# Delete with verbose output
find . -name "*.log" -exec rm -v {} \;
Summary
Always follow the three-step deletion pattern: (1) List files first with find, (2) Verify the matches are correct, (3) Then delete. For simple current-directory wildcards, rm *.pattern works fine. For anything complex—subdirectories, time-based deletion, multiple conditions—use find. Never use -delete without testing the pattern first with a plain find command. The few seconds spent verifying prevent hours of recovery from accidental deletion.