How do I recursively list all directories at a location, breadth-first?
Solution 1
The find
command supports -printf
option which recognizes a lot of placeholders.
One such placeholder is %d
which renders the depth of given path, relative to where find
started.
Therefore you can use following simple one-liner:
find -type d -printf '%d\t%P\n' | sort -r -nk1 | cut -f2-
It is quite straightforward, and does not depend on heavy tooling like perl
.
How it works:
- it internally generates list of files, each rendered as a two-field line
- the first field contains the depth, which is used for (reverse) numerical sorting, and then cut away
- resulting is simple file listing, one file per line, in the deepest-first order
Solution 2
If you want to do it using standard tools, the following pipeline should work:
find . -type d | perl -lne 'print tr:/::, " $_"' | sort -n | cut -d' ' -f2
That is,
- find and print all the directories here in depth first order
- count the number of slashes in each directory and prepend it to the path
- sort by depth (i.e., number of slashes)
- extract just the path.
To limit the depth found, add the -maxdepth argument to the find command.
If you want the directories listed in the same order that find output them, use "sort -n -s" instead of "sort -n"; the "-s" flag stabilizes the sort (i.e., preserves input order among items that compare equally).
Solution 3
You can use find command, find /path/to/dir -type d So below example list of directories in current directory :
find . -type d
Solution 4
My feeling is that this is a better solution than previously mentioned ones. It involves grep and such and a loop, but I find it works very well, specifically for cases where you want things line buffered and not the full find buffered.
It is more resource intensive because of:
- Lots of forking
- Lots of finds
- Each directory before the current depth is hit by find as many times as there is total depth to the file structure (this shouldn't be a problem if you have practically any amount of ram...)
This is good because:
- It uses bash and basic gnu tools
- It can be broken whenever you want (like you see what you were looking for fly by)
- It works per line and not per find, so subsequent commands don't have to wait for a find and a sort
- It works based on the actual file system separation, so if you have a directory with a slash in it, it won't be listed deeper than it is; if you have a different path separator configured, you still are fine.
#!/bin/bash depth=0 while find -mindepth $depth -maxdepth $depth | grep '.' do depth=$((depth + 1)) done
You can also fit it onto one line fairly(?) easily:
depth=0; while find -mindepth $depth -maxdepth $depth | grep --color=never '.'; do depth=$((depth + 1)); done
But I prefer small scripts over typing...
Solution 5
I don't think you could do it using built-in utilities, since when traversing a directory hierarchy you almost always want a depth-first search, either top-down or bottom-up. Here's a Python script that will give you a breadth-first search:
import os, sys
rootdir = sys.argv[1]
queue = [rootdir]
while queue:
file = queue.pop(0)
print(file)
if os.path.isdir(file):
queue.extend(os.path.join(file,x) for x in os.listdir(file))
Edit:
- Using
os.path
-module instead ofos.stat
-function andstat
-module. - Using
list.pop
andlist.extend
instead ofdel
and+=
operators.
Andrey Fedorov
Updated on December 24, 2020Comments
-
Andrey Fedorov over 3 years
Breadth-first list is important, here. Also, limiting the depth searched would be nice.
$ find . -type d /foo /foo/subfoo /foo/subfoo/subsub /foo/subfoo/subsub/subsubsub /bar /bar/subbar $ find . -type d -depth /foo/subfoo/subsub/subsubsub /foo/subfoo/subsub /foo/subfoo /foo /bar/subbar /bar $ < what goes here? > /foo /bar /foo/subfoo /bar/subbar /foo/subfoo/subsub /foo/subfoo/subsub/subsubsub
I'd like to do this using a bash one-liner, if possible. If there were a javascript-shell, I'd imagine something like
bash("find . -type d").sort( function (x) x.findall(/\//g).length; )