• Running macOS 10.13.6 and using bash 5.0.17(1)
  • A lot of subdirectories which hold multiple files.
  • Need to filter out files in subdirectories with a specific extension (.avi).
  • Need to process all .avi files and remux to .mp4 using ffmpeg. ffmpeg uses the following syntax for remuxing: ffmpeg -i in.avi -c copy out.mp4
  • Output format: in the same folder as the source .avi file, and using the same filename (apart from the .avi extension)

Example file structure:

$ find . -maxdepth 2

In this case I would like to filter out the files ./abc/abc.avi and ./123/123.avi , which I can do using regular expressions and find:

$ find -E . -iregex ".*\.avi"

The desired remuxed .mp4 output filenames would then be:


How can I:

  1. using a script, remux all these .avi files to .mp4 container with one command? I am not sure how to pipe the output of find to the input of ffmpeg, and at the same time specify the desired output filenames.
  2. delete the original .avi files, but only if the remux was successful?
  • You are using some advanced string manipulation technique there, viz. out=${in%.*}, which I avoided thinking it would be overly complex. Well done. Did not know that basename would not work in bash on MacOS. That's peculiar. You should still protect file removal wirh \rm instead of just rm. Good luck.
    – Cbhihe
    May 9, 2020 at 6:19

2 Answers 2


I'm not a user of ffmpeg and I am writing this answer from a bash environment on Arch Linux, but your two requirements can be met for example on FreeBSD and MacOS-X with:

$ from=avi; to=mp4; 
$ find -E . -type f -iregex ".*\.avi" -execdir \
  sh -c 'file=$(basename $3); (ffmpeg -i $file.$1 -c copy $file.$2) 2>/dev/null && \rm $3' shellproc "$from" "$to" {} \;

In the one liner above:

  • I included -type f before the -iregex .... It should save you time by stat'ing only files of the "regular file" type.
  • $1 stands for $from
  • $2 stands for $to
  • $3 stands for {}, i.e. the result of your find command
  • shellproc in ...shellproc "$from" "$to" {} \; is just an arbitrary tag that identifies the shell processes executed with: sh -c 'file=$(basename $3); (ffmpeg -i $file.$1 -c copy $file.$2) 2>/dev/null && \rm $3'. That tag will prefix any error message output by said shell. That's its function.

There are several possible ways of doing what you want. I'd recommend you have a look at an answer by Kusalananda, to get to know (much) more about find ... -exec...'s grammar.


Credit to Cbhihe's answer, as it got me on the right path. I needed to change some a few things because of the way baseline works on mac and the handling of spaces in filenames.

macOS find uses a somewhat special syntax. You can use the following one-liner:

find -E . -type f -iregex ".*\.avi" -execdir bash -c 'in=$1; out=${in%.*}.mp4; (ffmpeg -y -i "$in" -c copy "$out") 2>/dev/null && \rm "$in"' shellproc {} \;

  • find -E . -type f -iregex ".*\.avi" searches for files:
    • . indicates to search the current directory,
    • -E indicates extended find which allows to use regular expressions,
    • -iregex ".*\.avi" searches for all `.aviz files in this path, including subdirectories.
  • -execdir bash -c '<COMMAND>' shellproc {} \; executes a COMMAND using bash shell for every search result, executed from the same directory as the search result. The filename is passed on as argument $1.
  • in=$1; out=${in%.*}.mp4; (ffmpeg -y -i "$in" -c copy "$out") 2>/dev/null && \rm "$in" defines input and output filenames, runs ffmpeg to do the remuxing and suppresses the verbose ffmpeg output, and removes the input file only when the remuxing finished succesfully.

On Linux systems, the find syntax is a little different:

find . -type f -regex ".*\.avi" -execdir bash -c 'in=$1; out=${in%.*}.mp4; (ffmpeg -y -i "$in" -c copy "$out") 2>/dev/null && \rm "$in"' shellproc {} \;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .