How can I make a foreach * .mp3 file recursively in a bash script?

The following works fine in the current folder, but I would like it to also scan subfolders.

for file in *.mp3 do echo $file done 
+8
linux bash for-loop mp3
source share
6 answers

Too many of these answers use a shell extension to store search results. This is not something you should do lightly.

Say I have 30,000 songs, and the names of these songs are about 30 characters. Even if it does not fall into the problem of white space.

My find will return more than 1,000,000 characters, and it is very likely that my command line buffer is not so large. If I did something like this:

 for file in $(find -name "*.mp3") do echo "some sort of processing" done 

The problem (besides the space in the file names) is that your command line buffer will simply remove the overflow from find . It can even fail badly.

This is why the xargs team was created. It ensures that the command line buffer is never full. It will execute the command following xargs as many times as necessary to protect the command line buffer:

 $ find . -name "*.mp3" | xargs ... 

Of course, using xargs in this way will be flooded anyway, but modern xargs and find implementations have a way to solve this problem:

 $ find . -name "*.mp3 -print0 | xargs --null ... 

If you can guarantee that the file names will not have tabs or \n (or double spaces) in them, it is best to find a search in the while loop:

 find . -name "*.mp3" | while read file do 

The pipeline will send files to while read before the command line buffer is full. Even better, the read file is read all over the line and puts all the elements found on that line in $file . This is not ideal, because read still breaks into a space, so file names such as:

 I will be in \n your heart in two lines.mp3 I love song names with multiple spaces.mp3 I \t have \ta \t thing \t for \t tabs.mp3. 

Still fail. The $file variable is as follows:

 I will be in your heart in two lines.mp3 I love song names with multiple spaces.mp3 I have a thing for tabs.mp3. 

To get around this problem, you should use find ... -print0 to use zeros as input delimiters. Then either change IFS to use zeros, or use the -d\0 option in the while statement in the BASH shell.

+7
source share

There are many ways to trick this cat. I myself would use a call to the find command:

 for file in $(find . -name '*.mp3') do echo $file TITLE=$(id3info "$file" | grep '^=== TIT2' | sed -e 's/.*: //g') ARTIST=$(id3info "$file" | grep '^=== TPE1' | sed -e 's/.*: //g') echo "$ARTIST - $TITLE" done 

If you have spaces in file names, it is best to use the -print0 to search; one of the possible ways:

 find . -name '*.mp3' -print0 | while read -d $'\0' file do echo $file TITLE=$(id3info "$file" | grep '^=== TIT2' | sed -e 's/.*: //g') ARTIST=$(id3info "$file" | grep '^=== TPE1' | sed -e 's/.*: //g') echo "$ARTIST - $TITLE" done 

alternatively you can save and restore IFS . Thanks to the comments of David W. and, in particular, pointing out that the while version also has the advantage that it will process a very large number of files correctly, while the first version that extends $(find) loop will not be able to work in which This is because shell expansion has limitations.

+4
source share
 find . -name *.mp3 -exec echo {} \; 

The string {} is replaced by the current file name, which is processed wherever it occurs in the command arguments, and not only in those arguments where it is one, as in some versions of find.

Please check a person’s search for more information http://unixhelp.ed.ac.uk/CGI/man-cgi?find

+4
source share
 find . -name \*.mp3 | ( while read file; do echo $file done ) 
+3
source share

This works with most file names (including spaces), but not with new characters, tabs or double spaces.

 find . -type f -name '*.mp3' | while read i; do echo "$i" done 

This works with all file names.

 find . -type f -name '*.mp3' -print0 | while IFS= read -r -d '' i; do echo "$i" done 

But if you want to use only one command, you can use the xargs example:

 find . -type f -name '*.mp3' -print0 | xargs -0 -l echo 
+3
source share

It looks like you are looking for the find command. I have not tested this, but something like this:

 files=(`find . -name *.mp3`) for file in "${files[@]}"; do echo $file TITLE="id3info "$file" | grep '^=== TIT2' | sed -e 's/.*: //g'" ARTIST="id3info "$file" | grep '^=== TPE1' | sed -e 's/.*: //g'" done 

EDIT: using an array makes the command safe for files with spaces in their names.

-one
source share

All Articles