-1
find . -name "*.mov" -exec bash -c 'ffmpeg -i "{}" -c:v libx264 -crf 20 -pix_fmt yuv420p "${0/.mov}.mp4"' {} \;

Above command works perfectly when I run it manually but I could not put it within a function in the .bashrc file. Receiving unexpected end of line error.

function fff { find . -name "*.mov" -exec bash -c 'ffmpeg -i "{}" -c:v libx264 -crf 20 -pix_fmt yuv420p "${0/.mov}.mp4"' {} \; }
terdon
  • 242,166
misaligar
  • 101
  • 4
  • 7
    What does the function itself look like? – Nasir Riley Oct 13 '18 at 04:06
  • Without seeing the actual function definition, this is just a wild guess, but: are you missing a semicolon or line break before the } that closes the function definition? – Gordon Davisson Oct 13 '18 at 05:56
  • 2
    The in-line bash script has a code injection vulnerability. Never embed {} in shell code like that. Use bash -c '...' bash {} \; instead and then $1 in the in-line script. – Kusalananda Oct 13 '18 at 07:32
  • function fff { find . -name "*.mov" -exec bash -c 'ffmpeg -i "{}" -c:v libx264 -crf 20 -pix_fmt yuv420p "${0/.mov}.mp4"' {} \; } – misaligar Oct 13 '18 at 11:59
  • @Kusalananda, just to be clear (and I know you know this), one should use "$1" in the inline script, or the same vulnerability arrives by a different route. Here's a good answer by Stéphane Chazelas explaining that. – Toby Speight Feb 21 '22 at 10:50
  • 1
    @TobySpeight Yes, I was sloppy. Using $1 means using it quoted, if the context is such that it needs to be quoted. It's not quite the same vulnerabilities though, as you can't rewrite a script in the same way by just leaving the quotes off an expansion. With {} you actually insert code, so a filename with an odd number of quotes could cause a syntax error, or worse. – Kusalananda Feb 23 '22 at 07:18

1 Answers1

7

The function definition you gave in a comment:

function fff { find . -name "*.mov" -exec bash -c 'ffmpeg -i "{}" -c:v libx264 -crf 20 -pix_fmt yuv420p "${0/.mov}.mp4"' {} \; }

is missing a semicolon or line break before the } that's supposed to end the function definition. The escaped semicolon doesn't count; that's just an argument to find that ends the -exec primary. This should work:

function fff { find . -name "*.mov" -exec bash -c 'ffmpeg -i "{}" -c:v libx264 -crf 20 -pix_fmt yuv420p "${0/.mov}.mp4"' {} \; ; }
#                                                                                                             semicolon here: ^^^

...but there are still a couple of problems. First, as @Kusalananda said in a comment, injecting filenames directly into the shell command with {} isn't safe; you should pass it as an argument and use that (as "$1" etc). You're already doing that, but as $0 -- that's really supposed to be the command/function/etc name, not a regular argument, so it's better to pass something else (like find-bash) as $0, and have the actual argument be $1.

Second (again pointed out by @Kusalananda), using ${1/.mov} to remove the old ".mov" suffix might malfunction if the filename has ".mov" somewhere else in the name; ${1%.mov} will remove it specifically from the end.

Third (and much less importantly), function is a nonstandard bashism that many people prefer to avoid; the standard syntax is funcname() definition. I'd also prefer to use line breaks rather than semicolons to separate commands. With all these fixes in place, here's what I have:

fff() {
    find . -name "*.mov" -exec bash -c 'echo ffmpeg -i "$1" -c:v libx264 -crf 20 -pix_fmt yuv420p "${1%.mov}.mp4"' find-bash {} \;
}
  • 2
    It would also be better to use "${1%.mov}" to be sure that it's the filename suffix we're removing and not just some .mov anywhere in the file path. Also use bash or find-bash or something similar as $0 rather than -. This string would be used in any error messages generated by the embedded script. See https://unix.stackexchange.com/questions/156008 – Kusalananda Oct 19 '18 at 07:26
  • @Kusalananda Good points; stand by for an edit... – Gordon Davisson Oct 20 '18 at 01:01