The bash
shell can recurse into directories with its special **
globbing pattern. The **
pattern matches like *
, but also reaches across /
in pathnames:
shopt -s globstar nullglob dotglob
for dirpath in ./**/; do
printf '%s\n' "$dirpath"
# whatever other code needs to be run on "$dirpath"
done
I'm enabling globstar
to get access to **
, and I'm also enabling nullglob
and dotglob
to be able to skip the loop entirely if the pattern does not match anything , and to also see hidden files.
The pattern ./**/
would match any directory in or below the current directory, just like ./*/
would match any directory in the current directory.
Use ./**/*
or ./**
to list all types of files.
You can also do this portably with find
:
find . -type d -exec sh -c '
for dirpath do
printf "%s\n" "$dirpath"
# whatever other code needs to be run on "$dirpath"
done' sh {} +
Here, find
feeds the loop in the sh -c
script with pathnames of directories.
The only difference that you will see when running these two examples is that the first example (the bash
loop with **
) will resolve symbolic links to directories, while find
will not resolve symbolic link to directories.
If all you want is to list directories, then the find
example may be shortened significantly into
find . -type d -print
Use -type f
in place of -type d
to see only regular files, and delete the -type
test completely to see all types of files.
You could obviously also use ls
to get such a list, but I don't really see much point in it as you can't do anything but look at the output from ls
anyway.
shopt -s globstar dotglob
ls -1d ./**
Note that this command runs the risk of triggering an "argument list too long" error if the list of pathnames that the pattern expands to is too long. None of the other commands in this answer has that issue.
Also note that it isn't really ls
that does any recursing here, it's the shell expanding the ./**
pattern that recurses, and it does that to create the list of arguments for ls
before ls
is even invoked.
shopt -s globstar; for i in xxx/**; do echo "$i"; done
? Please provide an actual example – Chris Davies Apr 27 '21 at 16:27tree -fail
, that's the output I mean (i.e. showing one line per file with the full relative path to that file listed).shopt -s globstar; for i in xxx/**; do echo "$i"; done
is in the right direction, but ignores all.*
files (which are important for my requirements to process every file). – YorSubs Apr 27 '21 at 16:31ls
. – Chris Davies Apr 27 '21 at 16:33tree -fail
. It's just that, but I'll do it. I'm curious, why is parsing the output ofls
bad, I'm curious why that might be a problem? As you can see in the above, I get one line per item, whether it is a directory or a file, with the full relative path. It also shows all.*
files/directories. – YorSubs Apr 27 '21 at 16:37tree
installed so I can't quickly and easily review its output. You've an answer from Kusalananda so it's largely moot here, but for next time please consider including sample input – Chris Davies Apr 27 '21 at 17:05