find: missing argument to -exec
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/shecho $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:
$ lsfalse true$ find * -exec {} \; -and -printtrue
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.