I have a fundamental question about how bash works, and a related practical question.
Fundamental question: suppose I am in a directory that has three subdirectories: a, b, and c.
hen the code
for dir in $(ls)
do
echo $dir
done
spits out:
a b c
a b c
a b c
i.e, dir
always stores a list of all of the files/directories in my cwd
. My question is: why in the world would this be convenient? In my opinion it is far more useful and intuitive to have dir
store each element at a time, i.e I would want to have output
a
b
c
Also, as per one of the answers - it is wrong to use for dir in $(ls)
, but when I use for dir in $(ls -l)
I get even more copies of a b c
(more than there are directories/files in the cwd). Why is that?
My second question is practical: how do I loop over all the directories (not files!) in my cwd
that start with capital W? I started with
for dir in `ls -l W*`
but this fails because a) the reason in question 1 and b) because it doesn't exclude files. Suggestions appreciated.
Never ever parse the output of ls
like this (Why you shouldn't parse the output of ls(1)).
Also, your syntax is wrong. You don't mean ()
, you mean $()
.
That being said, to loop over directories starting with W you would do (or use the find
command instead, depending on your scenario):
for path in /my/path/W*; do
[ -d "${path}" ] || continue # if not a directory, skip
dirname="$(basename "${path}")"
do_stuff
done
As for the output you get from the evil ls-loop, it should not look like that. This is the expected output and demonstrates why you do not want to use ls in the first place:
$ find
.
./c
./a
./foo bar
./b
$ type ls
ls is hashed (/bin/ls)
$ for x in $(ls); do echo "${x}"; done
a
b
c
foo
bar
This should work:
shopt -s nullglob # empty directory will return empty list
for dir in ./*/;do
echo "$dir" # dir is directory only because of the / after *
done
To be recursive in subdirectories too, use globstar
:
shopt -s globstar nullglob
for dir in ./**/;do
echo "$dir" # dir is directory only because of the / after **
done
You can make @Adrian Frühwirths' method to be recursive to sub-directories by using globstar
too:
shopt -s globstar
for dir in ./**;do
[[ ! -d $dir ]] && continue # if not directory then skip
echo "$dir"
done
From Bash Manual:
globstar
If set, the pattern ‘**’ used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/’, only directories and subdirectories match.
nullglob
If set, Bash allows filename patterns which match no files to expand to a null string, rather than themselves.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With