/flags/find/find-exec-plus-vs-semicolon
find -exec {} + vs -exec {} \;: batch or one command per file
- tool
- find
- command
- find
- flag
- -exec command {} ; and -exec command {} +
- default
- no command is run; find only prints the matching paths
find . -name '*.txt' -exec wc -l {} + runs wc once with as many file names as fit on a command line; find . -name '*.txt' -exec wc -l {} \; runs wc once per file. Both replace {} with the path and both are POSIX. Use + by default: it is much faster on many files, and it works like xargs without any problem with spaces in names. Use \; when the command accepts only one file, or when {} must appear in the middle of the arguments.
The difference in one run
mkdir -p proj
touch proj/a.txt proj/b.txt 'proj/my file.txt'
echo '-- one invocation per file'; find proj -name '*.txt' -exec echo run: {} \; | sort
echo '-- one batch'; find proj -name '*.txt' -exec echo run: {} + | tr ' ' '\n' | grep -c '^run:'Output
-- one invocation per file run: proj/a.txt run: proj/b.txt run: proj/my file.txt -- one batch 1
The \; form starts echo three times; the + form starts it once with all three names. The backslash stops the shell from treating ; as the end of the shell command; ';' works too.
Exit status
mkdir -p ex; touch ex/a.txt
find ex -name a.txt -exec false {} \; ; echo "with \\; exit: $?"
find ex -name a.txt -exec false {} + ; echo "with + exit: $?"Output
with \; exit: 0 with + exit: 1
With \; the command's exit status is only used as the value of the test (true or false), so find still exits 0. With + any failing batch makes find exit non-zero when it finishes. In scripts with set -e, + is the one that reports failures.
Comparison
| -exec cmd {} \; | -exec cmd {} + | |
|---|---|---|
| Invocations | one per file | one per batch (limited by ARG_MAX) |
| Position of {} | anywhere, can repeat | last, right before + |
| Return value as a test | true if cmd exits 0 | always true |
| find exit status on cmd failure | unaffected | non-zero |
| Speed on 10,000 files | slow: 10,000 processes | fast: a handful of processes |
Because \; returns the command's status, it can act as a filter: find . -type f -exec grep -q TODO {} \; -print prints only the files that contain TODO.
Related actions
| Action | What it does |
|---|---|
| -execdir cmd {} ;/+ | Runs cmd from the directory of each match, with ./name; safer against path races (GNU and BSD) |
| -ok cmd {} ; | Like -exec ;, but asks for confirmation before each command |
| -print0 | xargs -0 | Same batching via a pipe, plus xargs -P for parallelism |
Errors and pitfalls
find . -exec echo {} x +Output · exit 1
find: missing argument to `-exec'
Anything after {} in the + form, or a missing terminator, produces missing argument to -exec. Other traps:
{}is not quoted by find. If the command is a shell (-exec sh -c '...' {} \;), pass the name as an argument (sh -c 'echo "$1"' sh {}) instead of pasting{}into the script, or a crafted file name can inject commands.- Shell features such as pipes or redirections do not work inside
-exec; wrap them insh -c. - The batch form never builds a command longer than the system limit, so it does not cause Argument list too long, unlike
cmd $(find ...). For NUL-safe pipelines see find -print0 | xargs -0.
FAQ
Why does find say missing argument to -exec?
The command is not terminated. End it with \; or with + right after {}, and make sure the shell passes the semicolon to find (escape or quote it).
Can I put text after {} when using +?
No. With + the {} must be the last argument before the +. To move files into a directory use GNU mv -t, as in find . -name '*.log' -exec mv -t old/ {} +.
Does find stop when the command fails?
No. find continues with the next file in both forms. With \; the failure is ignored for the exit status; with + find exits non-zero at the end if any invocation failed.