bash - redirect ls into custom script
Question
For college I am writting a script to read and display id3 tags in mp3 files. The arguments would be the files i.e
./id3.sh file1.mp3 file2.mp3 morefiles.mp3
I can read the arguments using $0, $1
etc. and get the number of args with $#
. how can I get it to read the output from a ls command?
ls *.mp3 | ./id3.sh
Solution
I would suggest using pipe and xargs
with -n argument, in the example below the id3.sh script will be called with at most 10 files listed by ls *.mp3
. This is very important, especially if you can have hounreds or thousands of files in the list. If you omit the -n 10
then your script will be called only once with the whole list. If the list is too long your system may refuse to run your script. You can experiment how much files in each invokation of your script to process (e.g. what is more efficient in your case).
ls *.mp3 | xargs -n 10 id3.sh
then you can read the files in your id3.sh script like this
while [ "$1" != "" ]; do
#next file available in ${1}
shift
done
OTHER TIPS
Try this:
ls *.mp3 | xargs id3.sh
The ls *.mp3 > ./id3.sh
command is going to overwrite your id3.sh script with the list of mp3's. You can try this:
./id3.sh `ls *.mp3`
EDIT: actually, what was I thinking? Is there a reason you just can't do this?
./id3.sh *.mp3
Any solution involving the expansion of *.mp3
risks failure if the number of .mp3 files is so large that the resultant expanded *.mp3
exceeds the shell's limit. The solutions above all have this problem:
ls *.mp3 | ...
for file in *.mp3; do ...
In fact, even though ls *.mp3|xargs ...
is a good start, but it has the same problem because it requires the shell to expand the *.mp3
list and use that list as command-line arguments to the ls
command.
One way to properly handle an arbitrary number of files is:
find . -maxdepth 1 -iname '*.mp3'|while read f; do
do_something_one_file_at_a_time.sh "$f"
done
OR:
find . -maxdepth 1 -iname '*.mp3' -print0|xargs -0 do_something.sh
(Both variants have the side benefit of properly handling filenames with spaces e.g. "Raindrops Keep Falling On My Head.mp3".
Note that in do_something.sh, you need to do for file in "$@"; do ...
and not just for file in $*;do ...
or for file in $@; do ...
.
Note also that amit_g's solution breaks if there are filenames with spaces.)
What's wrong with ./id3.sh *.mp3
? It's safer than any solution with ls
, and provides exactly the same globbing features. There's no need for xargs
here, unless you're using an old kernel and have enormous amounts of files.
./id3.sh *.mp3 # if the number of files is not too many
or
ls *.mp3 | xargs -n 10 ./id3.sh # if the number of files could be too many
then in the id3.sh
while [ "$1" != "" ]
do
filename=$1
#do whatever with $filename
shift
done