find: missing argument to -exec

BashShellFindArgumentsExec

Bash Problem Overview


I was helped out today with a command, but it doesn't seem to be working. This is the command:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 && rm {}\;

The shell returns

find: missing argument to `-exec'

What I am basically trying to do is go through a directory recursively (if it has other directories) and run the ffmpeg command on the .rm file types and convert them to .mp3 file types. Once this is done, remove the .rm file that has just been converted.

I appreciate any help on this.

Bash Solutions


Solution 1 - Bash

A -exec command must be terminated with a ; (so you usually need to type \; or ';' to avoid interpretion by the shell) or a +. The difference is that with ;, the command is called once per file, with +, it is called just as few times as possible (usually once, but there is a maximum length for a command line, so it might be split up) with all filenames. See this example:

$ cat /tmp/echoargs
#!/bin/sh
echo $1 - $2 - $3
$ find /tmp/foo -exec /tmp/echoargs {} \;
/tmp/foo - -
/tmp/foo/one - -
/tmp/foo/two - -
$ find /tmp/foo -exec /tmp/echoargs {} +
/tmp/foo - /tmp/foo/one - /tmp/foo/two

Your command has two errors:

First, you use {};, but the ; must be a parameter of its own.

Second, the command ends at the &&. You specified “run find, and if that was successful, remove the file named {};.“. If you want to use shell stuff in the -exec command, you need to explicitly run it in a shell, such as -exec sh -c 'ffmpeg ... && rm'.

However you should not add the {} inside the bash command, it will produce problems when there are special characters. Instead, you can pass additional parameters to the shell after -c command_string (see man sh):

$ ls
$(echo damn.)
$ find * -exec sh -c 'echo "{}"' \;
damn.
$ find * -exec sh -c 'echo "$1"' - {} \;
$(echo damn.)

You see the $ thing is evaluated by the shell in the first example. Imagine there was a file called $(rm -rf /) :-)

(Side note: The - is not needed, but the first variable after the command is assigned to the variable $0, which is a special variable normally containing the name of the program being run and setting that to a parameter is a little unclean, though it won't cause any harm here probably, so we set that to just - and start with $1.)

So your command could be something like

find -exec bash -c 'ffmpeg -i "$1" -sameq "$1".mp3 && rm "$1".mp3' - {} \;

But there is a better way. find supports and and or, so you may do stuff like find -name foo -or -name bar. But that also works with -exec, which evaluates to true if the command exits successfully, and to false if not. See this example:

$ ls
false  true
$ find * -exec {} \; -and -print
true

It only runs the print if the command was successfully, which it did for true but not for false.

So you can use two exec statements chained with an -and, and it will only execute the latter if the former was run successfully.

Solution 2 - Bash

Try putting a space before each ;

Works:

find . -name "*.log" -exec echo {} \;

Doesn't Work:

find . -name "*.log" -exec echo {}\;

Solution 3 - Bash

I figured it out now. When you need to run two commands in exec in a find you need to actually have two separate execs. This finally worked for me.

find . -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 \; -exec rm {} \;

Solution 4 - Bash

You have to put a space between {} and \;

So the command will be like:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 && rm {} \;

Solution 5 - Bash

Just for your information:
I have just tried using "find -exec" command on a Cygwin system (UNIX emulated on Windows), and there it seems that the backslash before the semicolon must be removed:
find ./ -name "blabla" -exec wc -l {} ;

Solution 6 - Bash

For anyone else having issues when using GNU find binary in a Windows command prompt. The semicolon needs to be escaped with ^

find.exe . -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 ^;

Solution 7 - Bash

You need to do some escaping I think.

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} \-sameq {}.mp3 \&\& rm {}\;

Solution 8 - Bash

Just in case anyone sees a similar "missing -exec args" in Amazon Opsworks Chef bash scripts, I needed to add another backslash to escape the ;

bash 'remove_wars' do
  user 'ubuntu'
  cwd '/'
  code <<-EOH
    find /home/ubuntu/wars -type f -name "*.war" -exec rm {} \\;
  EOH
  ignore_failure true
end

Solution 9 - Bash

Also, if anyone else has the "find: missing argument to -exec" this might help:

In some shells you don't need to do the escaping, i.e. you don't need the "" in front of the ";".

find <file path> -name "myFile.*" -exec rm - f {} ;

Solution 10 - Bash

Both {} and && will cause problems due to being expanded by the command line. I would suggest trying:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i \{} -sameq \{}.mp3 \; -exec rm \{} \;

Solution 11 - Bash

In my case I needed to execute "methods" from by bash script, which does not work when using -exec bash -c, so I add another solution I found here, as well:

UploadFile() {
  curl ... -F "file=$1"
}

find . | while read file;
  do
    UploadFile "$file"
  done

This thread pops up first when searching for solutions to execute commands for each file from find, so I hope it's okay that this solution does not use the -exec argument

Solution 12 - Bash

If you are still getting "find: missing argument to -exec" try wrapping the execute argument in quotes.

find <file path> -type f -exec "chmod 664 {} \;"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbsView Question on Stackoverflow
Solution 1 - BashMarianView Answer on Stackoverflow
Solution 2 - BashDustin CowlesView Answer on Stackoverflow
Solution 3 - BashAbsView Answer on Stackoverflow
Solution 4 - BashAzafoCossaView Answer on Stackoverflow
Solution 5 - BashDominiqueView Answer on Stackoverflow
Solution 6 - BashjansohnView Answer on Stackoverflow
Solution 7 - BashMatthew SmithView Answer on Stackoverflow
Solution 8 - BashJohn CooperView Answer on Stackoverflow
Solution 9 - BashsellmaurerView Answer on Stackoverflow
Solution 10 - BashVeeArrView Answer on Stackoverflow
Solution 11 - Bashhb0View Answer on Stackoverflow
Solution 12 - BashwooView Answer on Stackoverflow