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 &&
. Your 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 /)
:-)
(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 would 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.