Given the situation that audio needs to be extracted from video files residing in subdirectories, how would one go about properly naming the resulting files?
The following one-liner works, but retains the old file extension.
find -name "*.mp4" -exec ffmpeg -i {} -vn -acodec copy {}.aac -hide_banner \;Current outcome:
foobar.mp4 --> foobar.mp4.aacDesired outcome:
foobar.mp4 --> foobar.aac 2 Answers
There is no mechanism in find itself that allows you to get a substring from whatever is substituted for {}. Even adding a suffix (like you did: {}.aac) may not be supported. POSIX says:
A utility_name or argument containing only the two characters "{}" shall be replaced by the current pathname. If a utility_name or argument string contains the two characters "{}", but not just the two characters "{}", it is implementation-defined whether find replaces those two characters or uses the string without change.
With many implementations things like foo{}bar do work though, nevertheless be warned. To do something more you definitely need a tool that will manipulate the string; usually a shell. In your case:
find . -name "*.mp4" -exec sh -c 'ffmpeg -i "$1" -vn -acodec copy "${1%.*}.aac" -hide_banner' sh {} \;Here ${1%.*} is responsible for getting the pathname without extension.
You may be tempted to replace ffmpeg -i "$1" with ffmpeg -i "{}" inside the command string passed to sh. Don't do this. My other answer explains why the latter is wrong (and why find . -name … is better than find -name …).
Another way would be :
for file in $(find . -name "*mp4")
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%%.mp4}.aac
doneEDIT:I have no idea why I might have thought of such a loop, when the simpler one suffices:
for file in *.mp4
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%.mp4}.aac
doneHowever if a recursive find is really required, based on the comment by Scott I decided to improve the last answer. I use a while loop with input from find through process substitution:
while IFS= read -r file
do
ffmpeg -nostdin -i $file -vn -acodec copy [other parameters if required] ${file%.mp4}.aac
done< <(find . -name "*.mp4")Note that ffmpeg requires -nostdin in a while loop. exec and xargs in general may be more efficient than while loops, but since this instance would have ffmpeg do the processing, I don't feel there is a significant performance hit.