If I run this command:
sudo find . -name *.mp3
then I can get a listing of lots of mp3 files.
Now I want to do something with each mp3 file in a loop. For example, I could create a while
loop, and inside assign the first file name to the variable file
. Then I could do something with that file. Next I could assign the second file name to the variable file
and do with that, etc.
How can I realize this using a linux shell command? Any help is appreciated, thanks!
For this, use the read
builtin:
sudo find . -name *.mp3 |
while read filename
do
echo "$filename" # ... or any other command using $filename
done
Provided that your filenames don't use the newline (\n
) character, this should work fine.
My favourites are
find . -name '*.mp3' -exec cmd {} \;
or
find . -name '*.mp3' -print0 | xargs -0 cmd
While Loop
As others have pointed out, you can frequently use a while read
loop to read filenames line by line, it has the drawback of not allowing line-ends in filenames (who uses that?).
xargs
vs. -exec cmd {} +
Summarizing the comments saying that -exec
...+
is better, I prefer xargs because it is more versatile:
- works with other commands than just
find
- allows 'batching' (grouping) in command lines, say
xargs -n 10
(ten at a time)
- allows parallellizing, say
xargs -P4
(max 4 concurrent processes running at a time)
does privilige separation (such as in the OP's case, where he uses sudo find
: using -exec
would run all commands as the root user, whereas with xargs
that isn't necessary:
sudo find -name '*.mp3' -print0 | sudo xargs -0 require_root.sh
sudo find -name '*.mp3' -print0 | xargs -0 nonroot.sh
in general, pipes are just more versatile (logging, sorting, remoting, caching, checking, parallelizing etc, you can do that)
How about using the -exec
option to find?
find . -name '*.mp3' -exec mpg123 '{}' \;
That will call the command mpg123
for every file found, i.e. it will play all the files, in the order they are found.
for file in $(sudo find . -name *.mp3);
do
# do something with file
done