One very useful command for locating files and performing operations on them is find with the -exec option.

find [path] [arguments] -exec [command] {} \;

The part that’s tricky to remember is the escaped semicolon at the end.

Per-file vs batch mode

The \; terminator runs the command once per file found:

find . -name "*.log" -exec rm {} \;
# Equivalent to: rm file1.log; rm file2.log; rm file3.log

The + terminator batches files into fewer command invocations, which is faster:

find . -name "*.log" -exec rm {} +
# Equivalent to: rm file1.log file2.log file3.log

Use + when possible. Use \; when the command only accepts one argument or when you need per-file control.

Useful examples

Fix directory permissions without affecting files (the reason I wrote this post):

find /path -type d -exec chmod g+x {} +

Using chmod -R g+x would make regular files executable, which is rarely what you want.

Delete files older than 30 days:

find /tmp -type f -mtime +30 -exec rm {} +

Find and grep (when you need more control than grep -r):

find . -name "*.md" -exec grep -l "TODO" {} +

execdir

The -execdir variant runs the command from the directory containing the matched file, which is safer when filenames might contain special characters:

find . -name "*.bak" -execdir rm {} +