/errors/find/missing-argument-to-exec
find: missing argument to `-exec'
- tool
- find
- exit code
- 1
- versions
- GNU findutils 4.x
- os
- linux, windows
GNU find prints find: missing argument to `-exec' when the command after -exec is not terminated. find needs either \; (run the command once per file) or + (run it once with many files) to know where the command ends. The semicolon must be escaped or quoted because the shell otherwise treats it as the end of the whole find command: write find . -name '*.log' -exec rm {} \; or, faster, find . -name '*.log' -exec rm {} +.
Reproduce it
touch a.log b.log
find . -name '*.log' -exec echo {} ;Output · exit 1
find: missing argument to `-exec'
The shell removed the ;, so find received -exec echo {} with nothing to close it. A {} that is not directly before + fails the same way:
mkdir dest && touch a.log
find . -maxdepth 1 -name '*.log' -exec cp {} dest/ +Output · exit 1
find: missing argument to `-exec'
Fix
touch a.log b.log
echo '--- one command per file'
find . -name '*.log' -exec echo file: {} \; | sort
echo '--- one command for all files'
find . -name '*.log' -exec echo files: {} +Output
--- one command per file file: ./a.log file: ./b.log --- one command for all files files: ./a.log ./b.log
With +, find appends as many paths as fit on one command line, which is much faster for thousands of files. Order of the paths depends on the directory, so the block sorts the first output.
{} in the middle of the command
When the file name must not be last (copying into a directory), use an option that takes the target first, or a small shell:
mkdir dest && touch a.log b.log
find . -maxdepth 1 -name '*.log' -exec cp -t dest {} +
find . -maxdepth 1 -name '*.log' -exec sh -c 'for f; do cp "$f" "dest/$f.bak"; done' sh {} +
ls destOutput
a.log a.log.bak b.log b.log.bak
cp -t is GNU-only; the sh -c form works with any find. The extra sh fills $0, so the file names arrive in $@.
Other ways to lose the terminator
Gluing the semicolon to the braces does not work either: {}\; reaches find as the single word {};, which is not a terminator.
touch a.log
find . -name '*.log' -exec echo {}\;Output · exit 1
find: missing argument to `-exec'
The same happens when the find command is built inside a string, for example in ssh host "...", sh -c "..." or a Makefile recipe: each layer of quoting may strip one backslash. Switch to +, which needs no escaping at all, or check the arguments that finally reach find with set -x.
How to recognise the cause
| What you typed | Why find complains |
|---|---|
-exec rm {} ; | The shell used ; as a command separator, so find never saw it |
-exec rm {}\; | No space before the backslash; find sees {}; |
-exec rm {} \ ; | The backslash escapes the space, not the semicolon |
-exec cp {} dest/ + | With +, {} must come immediately before it |
-exec rm {} | The terminator was forgotten entirely |
What the error does not mean
The message is about the syntax of the expression, so find exits with status 1 before it visits any file: nothing was deleted, copied or changed. Once the command is terminated correctly, failures of the command itself are different: with \; the command's exit status only acts as a true or false test and find itself still exits 0, while with + a failing command makes find exit non-zero.
Prevent it
Prefer -exec cmd {} + whenever the command accepts many paths: it is faster and has no backslash to forget. For deletions, GNU find's -delete action needs no -exec at all. Test a new expression with -exec echo {} + or -print before replacing echo with a command that changes files.
| Form | Meaning |
|---|---|
| -exec cmd {} \; | Run cmd once per file |
| -exec cmd {} ';' | Same, with the semicolon quoted instead of escaped |
| -exec cmd {} + | Run cmd with many files at once; {} must be last |
| -execdir cmd {} \; | Run cmd from the directory containing the file |
| -ok cmd {} \; | Ask for confirmation before each command |
FAQ
Should I use \; or + at the end of -exec?
Use + when the command accepts several file names, because find then runs it a few times instead of once per file. Use \; when the command takes exactly one file or {} must appear more than once.
Is -exec cmd {} + the same as piping to xargs?
It is similar: both batch file names into as few commands as possible. -exec ... + handles names with spaces and newlines safely, which with xargs needs find -print0 | xargs -0.
Does macOS find print the same message?
No. BSD find on macOS reports the same mistake with a different text, but the fix is identical: terminate the command with an escaped semicolon or with {} +.