I don’t think -filter_complex
actually needs a quote, it just needs a single argument. Quote is used to make sure a string doesn’t split into multiple arguments when it contains spaces, and ()
doesn’t spawn a subshell, etc. Same reason as why "$f"
is used.
So in your example, you’re passing a literal quote to the command, e.g.
argv[0] = ffmpeg
argv[1] = -i
argv[2] = filename with space.mp4
argv[3] = -filter_complex
argv[4] = "scale=1920:1080"
# ...
…when what you want is to have argv[4] to be scale=1920:1080
(without quote).
In this case, remove the \"
from SCALE
(e.g., SCALE="scale=1920:1080"
) and use quote to prevent argument from breaking in ffmpeg
command instead (e.g., -filter_complex "$SCALE"
):
lowmp4() { find . -type f -iname '*.mp4' -exec sh -c 'SCALE="scale=1920:1080"; for f; do ffmpeg -i "$f" -filter_complex "$SCALE" -vcodec libx265 -crf 28 "${f%.mp4}low_q.mp4" && rm "$f" && mv "${f%.mp4}low_q.mp4" "$f"; done' sh {} + ; }
Further suggestion: instead of using -exec
and piping into sh
, using the output of find
in a loop is a likely better option (to avoid spawning sh
and doing a single-run loop for every file). Since SCALE doesn’t change between runs, it can also be moved out of the loop altogether.
So, possibly, something like this:
lowmp4() {
SCALE="scale='if(gt(iw,1920),1920,-1)':'if(gt(ih,1080),1080,-1)':force_original_aspect_ratio=decrease"
find . -type f -iname '*.mp4' | while read -r f; do
ffmpeg -nostdin -i "$f" -filter_complex "$SCALE" -vcodec libx265 -crf 28 "${f%.mp4}low_q.mp4"
rm "$f"
mv "${f%.mp4}low_q.mp4" "$f"
done
}
-nostdin
is needed here so ffmpeg doesn’t swallow stdin and result in while read
to run only once. You can make this into a single line by joining them with ;
.