Skip to main content

How to Delete Files Matching a Pattern in Bash

• 6 min read
bash file operations delete pattern matching

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

MethodPattern TypeSafetyBest For
*rm .patternWildcardTest firstSimple patterns
find -deleteComplex regexDry-run optionComplex patterns
find -exec rmComplex regexSafeMaximum control
rm -iAny patternInteractiveCritical files
rm -fAny patternDangerousForced 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 ls first
  • It’s a one-off command

Avoid wildcards when:

  • Searching subdirectories (use find instead)
  • Pattern is complex (use find instead)
  • Files might have special characters in names
  • Safety is critical (use find -delete with --dry-run instead)

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.