My way is typically to escape spaces with the magic $'\n'
variable:
IFS=$'\n';
for i in `find . -iname '*flac'`; do
ffmpeg -loop 1 -i AlbumCover.png -i "%" -c:v libx264 -tune \
stillimage -c:a aac -strict experimental -b:a 192k -pix_fmt yuv420p \
-shortest "$(echo "$i" | sed s/\.flac$/.mp4/)"
done
finds all your flac files ( recursively ), and then executes the subshell to replace .flac
with .mp4
in each filename. This subshell executes as part of the -c
command.
I have a ffmpeg mocked up ffmpeg[1] that seems to verify the parameters are correctly escaped for the ffmpeg command:
["-loop", "1", "-i", "AlbumCover.png", "-i", "%", "-c:v", "libx264", "-tune", "stillimage", "-c:a", "aac", "-strict", "experimental", "-b:a", "192k", "-pix_fmt", "yuv420p", "-shortest", "./File 1.mp4"]
for
loops are probably the single most useful flow control statement in bash, but for this kind of thing I'd steer towards find
and xargs
, which both support separating arguments with the \0
character rather than something that might exist in pesky filenames, such as spaces, newlines, and so on.
Assuming all the flacs are in your home directory, then:
find . -type f -iname '*.flac' -print0 | \
xargs -0 -I % bash -c \
'ffmpeg -loop 1 -i AlbumCover.png -i "%" -c:v libx264 -tune stillimage -c:a aac -strict experimental -b:a 192k -pix_fmt yuv420p -shortest "$(echo "%" | sed s/\.flac$/.mp4/)"'
We're
$ find . -type f -iname '*.flac' -print0 | xargs -0 -I % bash -c 'ffmpeg -loop 1 -i AlbumCover.png -i "%" -c:v libx264 -tune stillimage -c:a aac -strict experimental -b:a 192k -pix_fmt yuv420p -shortest "$(echo "%" | sed s/\.flac$/.mp4/)"'
["-loop", "1", "-i", "AlbumCover.png", "-i", "./File 1.flac", "-c:v", "libx264", "-tune", "stillimage", "-c:a", "aac", "-strict", "experimental", "-b:a", "192k", "-pix_fmt", "yuv420p", "-shortest", "./File 1.mp4"]
["-loop", "1", "-i", "AlbumCover.png", "-i", "./File 10.flac", "-c:v", "libx264", "-tune", "stillimage", "-c:a", "aac", "-strict", "experimental", "-b:a", "192k", "-pix_fmt", "yuv420p", "-shortest", "./File 10.mp4"]
["-loop", "1", "-i", "AlbumCover.png", "-i", "./File 2.flac", "-c:v", "libx264", "-tune", "stillimage", "-c:a", "aac", "-strict", "experimental", "-b:a", "192k", "-pix_fmt", "yuv420p", "-shortest", "./File 2.mp4"]
...
In this case, the single quotes escape the entire command nicely. Bash is invoked because we need a subshell to run after xargs
has done its replacement. And the combination of find
's -print0
and xarg
's -0
escapes every valid filename. I do, however, assume that the filename doesn't contain any quotes.
[1]
$ cat `which ffmpeg`
#!/usr/bin/ruby
puts ARGV.inspect