Reputation: 596
The following works fine within the current folder, but I would like it to scan sub folders as well.
for file in *.mp3
do
echo $file
done
Upvotes: 12
Views: 14858
Reputation: 1933
find . -name *.mp3 -exec echo {} \;
The string {}
is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find.
Please check the find man for further info http://unixhelp.ed.ac.uk/CGI/man-cgi?find
Upvotes: 4
Reputation: 33317
This works with most filenames (including spaces) but not newlines, tabs or double spaces.
find . -type f -name '*.mp3' | while read i; do
echo "$i"
done
This works with all filenames.
find . -type f -name '*.mp3' -print0 | while IFS= read -r -d '' i; do
echo "$i"
done
But if you only want to run one command you can use xargs
example:
find . -type f -name '*.mp3' -print0 | xargs -0 -l echo
Upvotes: 5
Reputation: 1635
There are lots of ways to skin this cat. I would use a call to the find command myself:
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 your filenames then it's best to use the -print0
option to find; one possible way is this:
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 David W.'s comments and, in particular, for pointing out that the while
loop version also has the benefit that it will handle very large numbers of files correctly, whereas the first version which expands a $(find)
into a for-loop will fail to work at some point as shell expansion has limits.
Upvotes: 9
Reputation: 107040
Too many of these answers use shell expansion to store the results of a find. This is not something you should do lightly.
Let's say I have 30,000 songs, and the titles of these songs average around 30 characters. Let's not even get into the white space problem for now.
My find will return over 1,000,000 characters, and it's very likely that my command line buffer isn't that big. If I did something like this:
for file in $(find -name "*.mp3")
do
echo "some sort of processing"
done
The problem (besides the white space in file names) is that your command line buffer will simply drop off the overflow from the find
. It may even fail absolutely silently.
This is why the xargs
command was created. It makes sure that the command line buffer never overflows. It will execute the command following the xargs
as many times as necessary to protect the command line buffer:
$ find . -name "*.mp3" | xargs ...
Of course, using xargs
this way will still choke on white space, but modern implementations of xargs
and find
have a way of handling this issue:
$ find . -name "*.mp3 -print0 | xargs --null ...
If you can guarantee that file names won't have tabs or \n
(or double spaces) in them, piping a find into a while loop is better:
find . -name "*.mp3" | while read file
do
The pipeline will send the files to the while read
before the command line buffer is full. Even better, the read file
reads in an entire line and will put all items found in that line into $file
. It isn't perfect because the read
still breaks on white 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 \t a \t thing \t for \t tabs.mp3.
Will still fail. The $file
variable aill see them as:
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.
In order to get around this problem, you have to use find ... -print0
to use nulls as input dividers. Then either change IFS
to use nulls, or use the -d\0
parameter in the while read statement in BASH shell.
Upvotes: 9
Reputation: 1774
Sounds like you are looking for the find command. I haven't tested this, but something along these lines:
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 the array makes the command safe for files with spaces in their names.
Upvotes: -1