How to remove extension from pathname passed as {} in `find -exec`?

2

2

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.aac

Desired outcome:

foobar.mp4 --> foobar.aac

Daktari

Posted 2019-04-17T15:56:01.787

Reputation: 43

Answers

3

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 …).

Kamil Maciorowski

Posted 2019-04-17T15:56:01.787

Reputation: 38 429

Your solution works. Thanks also for linking to your other answer -- it clearly explains some questions I had about this solution. Much appreciated. – Daktari – 2019-04-18T01:06:21.023

amazing, thank you. I used this for renaming gunzip files to append an extension – Merlin – 2019-07-30T18:50:31.420

-1

Another way would be :

for file in $(find . -name "*mp4")
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%%.mp4}.aac
done

Rajib

Posted 2019-04-17T15:56:01.787

Reputation: 2 406

1

(1) Why is looping over find's output bad practice?  (2) "*mp4""*.mp4"  (3) Security implications of forgetting to quote a variable in bash/POSIX shells.  (4) Using the double % in ${file%%.mp4} is unnecessary and confusing.  (5) General reference: Why does my shell script choke on whitespace or other special characters?

– Scott – 2019-12-20T06:20:22.013

@Scott thanks for that extensive thread link. – Rajib – 2019-12-22T10:29:18.707