find: missing argument to -exec

272,879

Solution 1

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 2

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 3

Try putting a space before each \;

Works:

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

Doesn't Work:

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

Solution 4

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

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 {} ;

Share:
272,879

Related videos on Youtube

Abs
Author by

Abs

Updated on April 16, 2022

Comments

  • Abs
    Abs about 2 years

    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.

    • jeyhun_mikayil
      jeyhun_mikayil about 2 years
      Put space before ' \ '
  • Abs
    Abs about 14 years
    Not sure if it will get the variable of the file to delete? Anyone know if this is the case?
  • Abs
    Abs about 14 years
    If the first command in exec succeeds and the second command in exec is executed, will it still have access to the {} variable to delete the right file?
  • Marian
    Marian about 14 years
    What do you thinik {} expands to? Unless it contains two dots or commas that will stay as it is.
  • Marian
    Marian about 14 years
    To test such things just add an echo before the commands and see what it does.
  • pjammer
    pjammer over 10 years
    The key seems to be Marian's line of "the ; is an arg on it's own" that is what did it for me, lightbulb wise and for my code sample. thanks.
  • harperville
    harperville about 9 years
    This trips me up quite often. One of these days, I'll add a space by default.
  • Eurospoofer
    Eurospoofer over 8 years
    That's about the best description i've read on -exec. It's extremely powerful but I always find it difficult to get the right syntax for it. This made a few things much clearer. Particularly wrapping the command in the separate shell. Nice. Thanks.
  • Kirby
    Kirby over 8 years
    I really confused. find /etc/nginx -name '*.conf' -exec echo {} ; and find /etc/nginx -name '*.conf' -exec echo {}\; gave the same result. :(
  • gniourf_gniourf
    gniourf_gniourf over 7 years
    Note that -and and -or are not portable. POSIX specifies -a and -o, and in fact -a is always assumed (hence not needed).
  • WebComer
    WebComer over 7 years
    For me it works just the opposite in Cygwin under Windows 7: no space before\ ; works, with space - doesn't. But if i remove \, with space before ; it works, and without space before ; it doesn't, just the way it described here.
  • mamacdon
    mamacdon over 6 years
    I'm running the Bash shell that comes with Git for Windows, and Dustin Cowles' answer works for me. In other words: no quotes, backslash escaped by semicolon, space after {}.
  • Charles Duffy
    Charles Duffy about 6 years
    The ; not being quoted or escaped here means it can be consumed by the shell, not read by find. Also, -f and - f are two different things.
  • Charles Duffy
    Charles Duffy about 6 years
    @Marian, echo is quite unreliable -- you can't tell the difference between echo "one argument" and echo one argument (the latter's output being false, as it's actually passing echo two completely separate arguments). Much better to do something like -exec bash -c 'printf "%q " "$@"' _ ffmpeg -i {} -sameq {}.mp3 \; -printf '\n', which will print output in a way that makes nonprintable characters visible so you can detect DOS newlines and other oddities.
  • Dominique
    Dominique over 5 years
    There's no difference in meaning, but in some cases you need to put the backslash, in some case you can't put it, and in again other cases you might choose.
  • frmdstryr
    frmdstryr over 5 years
    Also there must be a space before the terminator. Ex "\;" doesn't work but " \;" does
  • Snidhi Sofpro
    Snidhi Sofpro over 3 years
    For the benefit of future readers who encounter this error with ssh, enclosing the terminating escaped-semicolon after the -exec resolved the issue(Ubuntu 18.04.2 LTS): $ ssh remuser@remhost find Documents -exec echo {} '\;'
  • Pysis
    Pysis almost 3 years
    find: In ‘-exec ... {} +’ the ‘{}’ must appear by itself, but you specified ‘... {} ...’
  • cessor
    cessor almost 3 years
    Thank you so much, this was exactly my problem.