The find -exec option turns file searches into automated batch operations. Instead of manually processing files one by one, you define search criteria and an action, then find handles everything from backing up photos spread across nested directories to compressing logs older than 30 days or adding watermarks to hundreds of images at once.
This guide covers the core syntax, explains the difference between single-file execution (\;) and batched execution (+), and walks through practical examples including backups, bulk renaming, format conversion, remote syncing, and automated reporting. You will also learn when to use -execdir for safer operations in the matched directory, and how to preview destructive commands before running them. Pair this with our chmod command guide when you need to modify permissions in bulk, or our grep command guide for searching file contents.
Understand the find -exec Command
What find -exec Does
If find -exec is new to you, think of it as a command-line automation assistant that scouts for files matching your criteria and then runs a specific action on each one. The find command locates files based on name patterns, modification times, sizes, or permissions. The -exec option extends that by letting you process each match immediately without piping to xargs or writing a loop.
Syntax Breakdown
The basic structure combines find’s search capabilities with command execution:
find [path] [expression] -exec [command] {} \;
- [path]: Where to start searching. Use
/var/logto scan log directories,.for the current directory, or~for your home folder. - [expression]: What to search for. Examples:
-name "*.txt"finds text files,-mtime +30finds files older than 30 days,-size +100Mfinds files larger than 100 megabytes. - -exec [command]: The action to perform on each match. Use
ls -lhto list details,cpto copy,gzipto compress, or any command you would normally run manually. - {}: Placeholder replaced by each file’s full path. Every time find matches a file, it substitutes the actual filename here.
- \;: Marks the end of the -exec command. The backslash escapes the semicolon so your shell does not interpret it prematurely.
- +: Alternative terminator that batches multiple files into a single command invocation. Use
-exec ls -la {} +instead of\;when processing many files with read-only commands likelsorgrepto reduce overhead.
Understanding -exec vs -execdir
The -execdir option works like -exec but runs the command from the directory containing the matched file, not from where you invoked find. This matters for security and for commands that depend on relative paths.
With -exec, the {} placeholder contains the full path (e.g., /var/log/app/error.log). With -execdir, find changes to /var/log/app/ first, then {} contains just ./error.log. This prevents path-based exploits where a malicious filename could trick the command into operating on unintended files.
Use -execdir when:
- Moving or renaming files to locations relative to their current directory
- Running commands that expect to operate in the same directory as the file
- Processing untrusted filenames where path injection is a concern
Simple Example First
At its simplest, find -exec runs a command on every match. To list detailed information for all PDF files in your Documents folder:
find ~/Documents -name "*.pdf" -exec ls -lh {} \;
This searches ~/Documents, finds every file ending in .pdf, and runs ls -lh on each one. You see one line of output per PDF showing the size, permissions, and modification date:
-rw-r--r-- 1 user user 2.4M Jan 15 10:30 /home/user/Documents/report.pdf -rw-r--r-- 1 user user 156K Jan 12 08:15 /home/user/Documents/notes.pdf
Common find -exec Patterns by Task
The following table organizes typical find -exec scenarios by what you want to accomplish:
| Task | Expression & Command | What It Does |
|---|---|---|
| Back up files | -name "*.jpg" -exec cp {} {}.backup \; | Creates a .backup copy of every .jpg file found |
| Delete old logs | -name "*.log" -mtime +30 -exec rm {} \; | Removes log files older than 30 days |
| Compress files | -name "*.txt" -exec gzip {} \; | Compresses each text file with gzip |
| Change permissions | -type f -name "*.sh" -exec chmod +x {} \; | Makes all shell scripts executable |
| Move files | -type f -name "*.tmp" -execdir sh -c 'mkdir -p ./archive && mv "$1" ./archive/' _ {} \; | Moves temporary files into an archive subdirectory created alongside each match |
| Display file details | -name "*.conf" -exec ls -lh {} \; | Shows size and permissions for config files |
| Rename files | -name "*.html" -exec sh -c 'mv "$1" "${1%.html}.htm"' _ {} \; | Changes .html extensions to .htm |
| Search file contents | -name "*.cfg" -exec grep -H "error" {} \; | Finds lines containing “error” in config files |
Use \; when you need one command execution per file, which is safer for operations that modify files. Use + when processing many files with read-only commands like ls or grep to reduce process spawning overhead.
The “Delete old logs” pattern permanently removes files. Always test with
-exec rmfirst to verify which files will be affected before running the actual deletion.
Check and Install find on Linux
Most Linux distributions ship with GNU findutils pre-installed, but minimal containers, Alpine-based images, or custom rescue environments might not include it. Verify the version first before relying on advanced options.
Check if find is Installed
Run this command to confirm find is available and check its version:
find --version
find (GNU findutils) 4.9.0 Copyright (C) 2022 Free Software Foundation, Inc.
If you see version output starting with “GNU findutils,” you have the full toolset with all features covered in this guide. If the command is not found, install the package for your distribution.
Install find on Linux Distributions
Ubuntu and Debian-based distributions:
sudo apt install findutils
Fedora, RHEL, Rocky Linux, and AlmaLinux:
sudo dnf install findutils
Arch Linux and Manjaro:
sudo pacman -S findutils
openSUSE:
sudo zypper install findutils
BusyBox find Limitations
On Alpine Linux and other minimal distributions, the default BusyBox find supports basic options including -exec {} \; and -exec {} +, but lacks GNU-specific extensions. The most notable missing feature is -printf, which formats output in custom ways.
If you need -printf for advanced output formatting (used in the rsync example later in this guide), install the full GNU findutils package:
apk add findutils
Practical Examples Using the find -exec Command Option
Backing Up Files with find -exec
When you need to back up specific file types before making changes, find -exec lets you create copies without manually selecting each file. This is useful before running batch updates or testing new scripts that modify originals.
To find and create backups of all .jpg files in the /pictures directory:
find /pictures -type f -name "*.jpg" -exec cp {} {}.backup \;
This command locates each .jpg file and creates a backup by copying each file to a new file with the .backup extension.
Renaming File Extensions Using find -exec
Bulk renaming file extensions is common when migrating projects or standardizing naming conventions across a codebase. The shell’s parameter expansion feature ${1%.html}.htm strips the old extension and adds the new one. For more file moving and renaming techniques, see our mv command guide.
To change the extension of all .html files to .htm in the /web directory:
find /web -type f -name "*.html" -exec sh -c 'mv "$1" "${1%.html}.htm"' _ {} \;
This command renames each .html file, replacing the extension with .htm.
Converting Image Formats with find -exec
When you need to convert image formats in bulk for web optimization or compatibility, find -exec can process entire directories at once. This example requires ImageMagick to be installed (available in most distribution repositories).
To convert all .png images to .jpg in the /images directory:
find /images -type f -name "*.png" -exec sh -c 'convert "$1" "${1%.png}.jpg"' _ {} \;
This wraps convert (part of ImageMagick) in a tiny shell so each .png gains a matching .jpg copy like photo.jpg while leaving the original PNGs untouched.
Compressing Log Files: A find -exec Approach
System logs accumulate rapidly and consume disk space. Compressing older logs preserves history while freeing capacity. The -mtime +7 flag targets files modified more than 7 days ago, ensuring recent logs stay accessible for active troubleshooting.
To find and compress all .log files older than 7 days in /var/log:
find /var/log -type f -name "*.log" -mtime +7 -exec gzip {} \;
This command selects .log files older than 7 days and compresses them using gzip.
Removing Empty Directories with find -exec
Empty directories clutter file systems and complicate backups. The -type d -empty expression isolates directories containing no files or subdirectories. For more directory removal options, see our rmdir command guide.
To find and remove all empty directories in the /data directory, use the -delete action which handles the bottom-up ordering automatically:
find /data -type d -empty -delete
The -delete action is safer than -exec rmdir {} \; because it processes directories in depth-first order, removing children before parents. Without this ordering, you would get errors when find tries to remove a parent directory before its empty subdirectories.
If you prefer the explicit -exec approach, add -depth to force bottom-up processing:
find /data -depth -type d -empty -exec rmdir {} \;
Advanced Use Cases for the find -exec Option
The following examples cover scenarios beyond basic file operations: remote syncing, date stamping, automated reporting, and batch image processing.
Syncing Files to Remote Servers: Advanced find -exec Usage
When you need to back up specific file types to a remote server, combining find with rsync provides efficient incremental transfers. This pattern works well in cron jobs for automated off-site backups.
The following pipeline prints each match relative to /local/docs and hands the entire list to rsync in one invocation, so the directory structure stays intact and files with identical names do not overwrite each other:
find /local/docs -type f -name "*.pdf" -printf '%P\0' | rsync -av --from0 --files-from=- /local/docs/ user@remote_server:/remote/docs/
find uses -printf '%P\0' to emit a null-terminated list of PDFs relative to /local/docs. rsync reads that list with --from0 and --files-from=-, preserves the directory hierarchy under /local/docs, and transfers only the files that changed since the last run.
Date Stamping File Names: A find -exec Technique
Adding timestamps to filenames helps track versions and maintain chronological organization. The $(date +%Y%m%d) command inserts the current date in YYYYMMDD format, while dirname and basename preserve the original directory structure.
To add a current date stamp to the filenames of all .csv files in /data/reports:
find /data/reports -type f -name "*.csv" -exec sh -c 'mv "$1" "$(dirname "$1")/$(date +%Y%m%d)-$(basename "$1")"' _ {} \;
This command locates .csv files and renames each by prefixing the current date, enhancing file organization and version control.
Generating Large File Reports via find -exec
Monitoring disk usage proactively prevents capacity emergencies. This command identifies files consuming significant space and sends a detailed report to administrators, useful for scheduled capacity audits. For more disk analysis techniques, see our du command guide.
To find files larger than 100MB in /home and email a report:
find /home -type f -size +100M -exec ls -lh {} \; | mail -s "Large Files Report" admin@example.com
This command identifies files over 100MB, lists their details, and sends this information via email.
The
mailutilson Debian/Ubuntu ormailxon RHEL-based systems. If email is not configured, redirect output to a file instead:find ... > /tmp/large-files-report.txt
Automated Image Watermarking with find -exec
Protecting image copyrights at scale requires batch watermarking. This example uses ImageMagick’s composite tool to overlay a watermark at 30% opacity in the southeast corner of each image.
To add a watermark to all .jpg images in /images/gallery:
find /images/gallery -type f -name "*.jpg" -exec composite -dissolve 30% -gravity southeast watermark.png {} {} \;
This uses the composite command (part of ImageMagick) to overlay a watermark image on each .jpg file, crucial for copyright protection and branding. Install ImageMagick via your package manager if the composite command is not available.
Directory Creation Based on File Names Using find -exec
Organizing media libraries often requires creating folders matching file names. The basename command extracts the filename without extension, and mkdir -p creates the directory if it does not already exist.
To create directories based on the names of .mp4 files in /videos:
find /videos -type f -name "*.mp4" -exec sh -c 'mkdir -p "/archive/$(basename "$1" .mp4)"' _ {} \;
This pattern passes each matched path to the shell as $1, so basename safely strips the .mp4 extension even when filenames contain spaces, and mkdir -p builds the matching directory under /archive. For more directory management tips, see our mkdir command guide.
Troubleshooting Common find -exec Errors
Missing Argument to -exec
If you see this error:
find: missing argument to `-exec'
The -exec option requires a command terminator. You either forgot the \; or + at the end, or the shell consumed the semicolon before find could see it. Always escape the semicolon with a backslash:
find . -name "*.txt" -exec ls -la {} \;
Single quotes around the semicolon also work: -exec ls -la {} ';'
Permission Denied Errors
When find encounters directories you cannot read, it reports permission errors:
find: '/root': Permission denied find: '/var/lib/private': Permission denied
To suppress these errors and continue processing accessible files, redirect stderr:
find / -name "*.conf" -exec ls -la {} \; 2>/dev/null
Alternatively, run with sudo if you need to search protected directories.
Files with Spaces or Special Characters
Filenames containing spaces, quotes, or special characters can break commands if not handled properly. When using sh -c, always quote the variable:
# Wrong - breaks on filenames with spaces
find . -name "*.txt" -exec sh -c 'cat $1' _ {} \;
# Correct - quotes protect the filename
find . -name "*.txt" -exec sh -c 'cat "$1"' _ {} \;
The underscore (_) before {} is a placeholder for $0 in the subshell, ensuring the filename becomes $1.
rmdir Fails on Non-Empty Directories
If you see this error when removing directories:
rmdir: failed to remove '/path/dir': Directory not empty
The directory contained files when find checked it or a parent directory was processed before its children. Use -delete or add -depth for proper ordering:
find /data -depth -type d -empty -exec rmdir {} \;
-printf Not Recognized
If you see:
find: unrecognized: -printf
You are using BusyBox find (common on Alpine Linux) which does not support -printf. Install GNU findutils or use an alternative approach:
# Instead of: find . -name "*.pdf" -printf '%P\n'
# Use:
find . -name "*.pdf" -exec basename {} \;
FAQ
Use find -exec when you need simple, single-file operations or when filenames might contain newlines. Use xargs when you need parallel processing with -P, when processing millions of files where the batching overhead of -exec + is noticeable, or when you need more control over argument placement. For most everyday tasks, find -exec is simpler and safer.
Use -ok instead of -exec. The -ok option prompts you with y/n for each file before running the command. This is useful when deleting or modifying files where you want to review each match individually. Example: find /data -name "*.tmp" -ok rm {} \; prompts before each deletion.
Conclusion
The find -exec command transforms file searches into automated batch operations, from simple backups to complex multi-step transformations. The key patterns to remember: use {} as the file placeholder, choose \; for single-file execution or + for batched performance, wrap shell expansions in sh -c with properly quoted variables, and always preview destructive operations with -print before committing to -exec rm or -delete.
Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags:
<code>command</code>command<pre>block of code</pre><strong>bold</strong><em>italic</em><a href="URL">link</a><blockquote>quote</blockquote>