Linux find Command: Complete Guide with Real Examples

Tested on: Ubuntu 26.04 LTS · Debian 12 · Fedora 44 · Arch Linux — Last updated: June 2026

The find command is one of the most powerful and universally available tools in Linux. It traverses the filesystem in real time, filtering by name, type, size, modification time, permissions, ownership, and more — then optionally executes arbitrary commands on every match. Most engineers know find . -name "*.txt" and stop there. This guide covers the full surface area: combining conditions, executing actions, integrating with xargs, and the real-world patterns that eliminate tedious manual work.

Contents
  1. Prerequisites
  2. Basic Syntax
  3. Find by Name
  4. Find by Type
  5. Find by Size
  6. Find by Modification Time
  7. Find by Permissions and Ownership
  8. Combining Conditions
  9. Limiting Search Depth
  10. Executing Commands with -exec
  11. Find and Delete
  12. Find + xargs: Efficient Batch Processing
  13. More Real-World Recipes
    1. Find All Config Files Changed This Week
    2. Find Duplicate Filenames Across Different Directories
    3. Find Files With Broken Symbolic Links
    4. Find Recently Changed Files After a Deployment
    5. Find World-Readable Private Keys (Security Audit)
  14. find vs locate vs fd
    1. Further Reading

Prerequisites

find ships with every Linux distribution as part of GNU findutils — no installation required. The examples below assume:

  • A standard bash shell
  • Basic terminal familiarity (navigating directories, running commands)
  • Root or sudo access for system-wide searches

Optional tools referenced later: xargs (part of findutils), mlocate, and fd-find.

Basic Syntax

The general form is straightforward — path first, then conditions, then actions:

find [path...] [options] [expression]

The expression is evaluated left to right. Every condition that isn't joined by an explicit -o (OR) is implicitly ANDed together. Some quick orientation examples:

# Walk the current directory tree — prints everything
find .

# Walk a specific path
find /var/log

# Search multiple paths simultaneously
find /home /tmp -name "*.log"

# Suppress "Permission denied" errors (common when searching as non-root)
find / -name "*.conf" 2>/dev/null

Find by Name

# Exact match (case-sensitive)
find . -name "config.yml"

# Case-insensitive
find . -iname "readme.md"

# Wildcard: any .log file
find /var/log -name "*.log"

# Anything starting with "error"
find . -name "error*"

# Substring match anywhere in the name
find . -name "*backup*"

# Match by extension in a project directory
find ~/projects -name "*.py"

A common mistake: quoting is required around glob patterns. Without quotes the shell expands *.log before find ever sees it, giving unexpected results or a "no such file" error.

Find by Type

# Regular files only
find . -type f

# Directories only
find . -type d

# Symbolic links
find . -type l

# Block devices (useful on /dev)
find /dev -type b

# Empty files
find . -type f -empty

# Empty directories (useful before cleanup)
find . -type d -empty

Find by Size

Size suffixes: c = bytes, k = kilobytes (1024), M = megabytes, G = gigabytes. Prefix + means "more than", - means "less than", no prefix means exactly that size (rounded up to the next block).

# Files larger than 100 MB
find . -size +100M

# Files smaller than 1 KB
find . -size -1k

# Files between 1 MB and 100 MB
find . -size +1M -size -100M

# Hunt down disk hogs anywhere on the system
find / -type f -size +500M 2>/dev/null

# Find surprisingly large files in a web root
find /var/www -type f -size +50M

To find the ten largest files on the system with human-readable sizes:

find / -type f -printf "%s %pn" 2>/dev/null | sort -rn | head -10 | numfmt --to=iec --field=1

Sample output:

8.1G /var/lib/docker/overlay2/abc123.../merged/large-model.bin
2.3G /home/alice/Videos/recording.mkv
1.7G /var/log/journal/abc.../system.journal
...

Find by Modification Time

Three time flags, all taking an integer number of 24-hour periods by default:

  • -mtime — last content modification
  • -atime — last access
  • -ctime — last metadata change (permissions, owner)

For minute-level precision, use -mmin, -amin, -cmin.

# Modified in the last 7 days
find . -mtime -7

# Modified more than 30 days ago (archive candidates)
find . -mtime +30

# Modified exactly 1 day ago (unusual — use -mtime -1 in practice)
find . -mtime 1

# Modified in the last 60 minutes
find . -mmin -60

# Modified more than 2 hours ago
find . -mmin +120

# Newer than a reference file — great for "what changed after this deploy?"
find . -newer /etc/passwd

# Find files modified since the last git commit
find . -newer .git/COMMIT_EDITMSG -type f -not -path "./.git/*"

Find by Permissions and Ownership

Permission-based searches are critical for security audits. The -perm flag accepts octal or symbolic notation. Prefix / means "any of these bits", prefix - means "all of these bits must be set".

# World-writable files — a security concern on shared systems
find / -type f -perm -o=w 2>/dev/null

# SUID binaries — can execute as file owner (often root)
find / -perm /4000 -type f 2>/dev/null

# SGID binaries
find / -perm /2000 -type f 2>/dev/null

# Both SUID and SGID in one pass
find / -perm /6000 -type f 2>/dev/null

# Files with exact permissions 644
find . -perm 644

# Files owned by a specific user
find /home -user alice

# Files owned by a specific group
find /var/www -group www-data

# Files NOT owned by root (unexpected in /etc is suspicious)
find /etc -not -user root

# Unowned files (owner UID no longer exists)
find / -nouser 2>/dev/null

Combining Conditions

By default, all conditions are ANDed — every one must match. Use -o for OR, and parentheses (escaped for the shell) to group logic.

# AND — implicit, both must match
find . -name "*.log" -size +10M

# OR — either condition matches
find . -name "*.jpg" -o -name "*.png"

# NOT
find . -not -name "*.txt"
find . ! -name "*.txt"     # equivalent shorthand

# Grouping with parentheses (must be escaped or quoted)
find . ( -name "*.jpg" -o -name "*.png" ) -size +1M

# Real-world: Python files modified this week, outside .git and __pycache__
find . -name "*.py" -mtime -7 
  -not -path "*/.git/*" 
  -not -path "*/__pycache__/*"

# Config files in /etc changed by non-root users in the last day
find /etc -name "*.conf" -mtime -1 -not -user root

Limiting Search Depth

# Current directory only — no recursion
find . -maxdepth 1 -name "*.txt"

# Up to 2 levels deep
find . -maxdepth 2 -type f

# Skip the top level, search from depth 2 to 4
find . -mindepth 2 -maxdepth 4 -name "*.conf"

# Find only top-level directories (project listing)
find /opt -mindepth 1 -maxdepth 1 -type d

Executing Commands with -exec

-exec runs a command for each match. {} is replaced by the current filename. The command must terminate with ; (one process per file) or + (batched — much faster for large result sets).

# Show detailed info for each found file
find . -name "*.log" -exec ls -lh {} ;

# Delete files older than 30 days in /tmp
find /tmp -mtime +30 -exec rm {} ;

# Compress old log files in place
find /var/log -name "*.log" -mtime +7 -exec gzip {} ;

# Change permissions on all shell scripts
find . -name "*.sh" -exec chmod +x {} ;

# Copy matching files to a backup directory
find . -name "*.conf" -exec cp {} /backup/ ;

# Using + batches all matches into one command call — faster
find . -name "*.txt" -exec wc -l {} +

# PHP malware scan — find files containing eval(
find /var/www -name "*.php" -exec grep -l "eval(" {} ;

Find and Delete

-delete is faster than -exec rm because it doesn't fork a new process. Always preview with -print before committing.

# Safe preview first — see exactly what will be deleted
find /tmp -mtime +30 -print

# Then delete
find /tmp -mtime +30 -delete

# Delete temp files by pattern
find . -name "*.tmp" -delete

# Remove empty directories after a cleanup
find . -type d -empty -delete

# Remove .DS_Store files left by macOS clients on a Linux server
find /var/www -name ".DS_Store" -delete

Find + xargs: Efficient Batch Processing

xargs reads filenames from stdin and passes them to a command in batches, avoiding the per-file process overhead of -exec {} ;. Always use -print0 / -0 together to handle filenames containing spaces or newlines.

# Count lines across all Python files
find . -name "*.py" -print0 | xargs -0 wc -l

# Find config files containing a specific string
find . -name "*.conf" -print0 | xargs -0 grep -l "database"

# Safe mass delete with space-proof filenames
find . -name "*.tmp" -print0 | xargs -0 rm

# Process files in parallel — 4 workers converting images
find . -name "*.jpg" -print0 | xargs -0 -P 4 -I {} convert {} {}.webp

# Archive old logs into a single tarball
find /var/log -name "*.log" -mtime +7 -print0 | 
  xargs -0 tar -czf /backup/old-logs-$(date +%Y%m%d).tar.gz

More Real-World Recipes

Find All Config Files Changed This Week

find /etc -name "*.conf" -mtime -7 -exec ls -lh {} ;

Find Duplicate Filenames Across Different Directories

find . -type f -printf "%fn" | sort | uniq -d

Find Files With Broken Symbolic Links

find . -type l ! -exec test -e {} ; -print

Find Recently Changed Files After a Deployment

# Everything touched in the last 10 minutes
find /var/www -type f -mmin -10

Find World-Readable Private Keys (Security Audit)

find / -name "*.pem" -o -name "id_rsa" -o -name "*.key" 2>/dev/null | 
  xargs -I {} stat -c "%a %U %n" {} | grep -v "^[46]00"

find vs locate vs fd

ToolMethodSpeedReal-time?Notes
findLive filesystem scanModerateYesAlways available, full feature set
locatePre-built indexVery fastNo (cron-updated)Won't see files created since last updatedb
fdLive filesystem scanFaster than findYesRespects .gitignore, friendlier syntax
# Install and use locate
sudo apt install mlocate         # Ubuntu/Debian
sudo dnf install mlocate         # Fedora
sudo updatedb                    # build/update the index
locate nginx.conf

# Install fd
sudo apt install fd-find         # Ubuntu/Debian — binary is fdfind
sudo dnf install fd-find         # Fedora
sudo pacman -S fd                 # Arch — binary is fd

# fd examples
fdfind "*.py" ~/projects         # Ubuntu/Debian (use 'fd' on Arch/Fedora)
fd -t d node_modules             # find node_modules directories
fd -e log --older-than 7d -x rm  # delete .log files older than 7 days

Use locate when you need speed and don't care if the file was created in the last hour. Use fd when working in codebases — its .gitignore awareness eliminates noise from build artifacts. Use find for everything else: scripting, cron jobs, permission aud


Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Learn more