Execute Command on All Files in Directory Structure by Extension

I’ve been looking into converting a bunch of OGG files to MP3 files for a friend using BASH.  As the files are organised in a directory structure, I need to traverse the directory structure, and run lame against each file to convert it.  Having a look around for BASH scripts to traverse a directory structure, I found someone with a similar problem who solved it using the “find” command.  I’ve taken their solution and added filtering by file extension.  This solution traverses a directory structure, filters the results using a regular expression, and then executes a command against each of the filtered results.  In general the command works something like this:

find /home/music -iregex ^.*\\.ogg$ -exec mysweetcommand '{}' \;


In the above example, we’re traversing /home/music and all of its sub-directries. The -iregex flag allows us to use a case insensitive regular expression to filter the files under /home/music and only return those with “.ogg” as the file extension. If we wanted to use a case sensitive regular expression we could use the -regex flag instead. Finally, the -exec flag is used to execute a command on each file returned after the filter has been applied. In this case we’re running a made up command. Something to note with the command is the use of ‘{}’ (including quotes). This is used as a placeholder for the current file.

I’m guessing you could write your own bash script and reference that in the -exec flag, using ‘{}’ as an argument to your script. Alternatively, you could use a variation on the find command above as part of a bash script. For example, the following script loops through the files returned by a find statement. It then outputs the filename with the extension switched. Please note that this has only been tested on Mac OS X, and not thoroughly. Furthermore, if you’re using a version of sed that supports case insensitive regular expressions (the default version on OS X doesn’t), the ‘i’ flag could be added to make the search and replace of file extensions case insensitve.


#!/bin/bash

EXTIN="ogg"
EXTOUT="mp3"

#If sed supports case insensitive searches, can use following line instead
#REGEXP="s/\.${EXTIN}$/.${EXTOUT}/gi"
REGEXP="s/\.${EXTIN}$/.${EXTOUT}/g"

for i in $( find /home/music -iregex ^.*\\.`echo $EXTIN`$ )
do
  OUTFILE="`echo $i | sed ${REGEXP}`"
  echo $OUTFILE
done

exit 0

There are probably GUI programs out there that do this more easily, but hey, where’s the fun in that!

**EDIT**
Thinking about this problem again it would seem that using ‘find’ as part of a for loop is probably quite inefficient.  I don’t know the intricacies of how BASH works, however in the code example above I would think that the find statement was evaluated first, and the results stored in memory.  Then, the for loop would iterate over the results of the find statement, thus iterating over them twice.  Instead, I think it would be more efficient to go with the other method suggested, where a BASH script is used with the <em>-exec</em> flag.  This way, the results are only parsed once.  Obviously you would then do away with the for loop in the above script, and simply execute your commands, substituting the result of the find statement with the first argument to the script (assuming that’s how the script was called).  In the case above, the $i variable would be replaced by $1, which is a special variable that stores the first argument to a script.

0 comments ↓

There are no comments yet...Kick things off by filling out the form below.

Leave a Comment