Your error lies in using find
in a command substitution. A command substitution always results in a single string. If you leave the substitution unquoted, that string will undergo splitting on spaces, tabs and newlines (by default). The split-up words will be processed for filename globbing. The resulting strings are what your loop will iterate over, which is not what you want.
Your loop that works for the current directory (fixing the issue with filenames starting with a dash):
for video in ./*.mkv; do
ffmpeg -i "$video" -acodec aac -ac 2 "${video%.*}".mp4
rm "$video"
done
Changed to work recursively, by enabling the **
globbing pattern (and using nullglob
to avoid running the loop at all if there are no matches):
shopt -s globstar nullglob
for video in ./*/.mkv; do
ffmpeg -i "$video" -acodec aac -ac 2 "${video%.*}".mp4
rm "$video"
done
The two loops above would not care about the filetype and would happily feed ffmpeg
with symbolic links, directories, or any other non-regular type of file, as long as the name matched the given pattern.
Alternatively, delegating the recursive generation of pathnames to find
(also only selecting regular files):
find . -name '*.mkv' -type f -exec sh -c '
for video do
ffmpeg -i "$video" -acodec aac -ac 2 "${video%.*}".mp4
rm "$video"
done' sh {} +
The above would also find hidden names matching the pattern, which the first two shell loops would not do (unless you enable the dotglob
shell option).
Other relevant questions:
ffmpeg
consumes its input, so you need to work around that (see BashFAQ #89: "I'm reading a file line by line and running ssh or ffmpeg, only the first line gets processed!"). – Gordon Davisson Apr 29 '22 at 19:15