Wednesday, September 04, 2013

Bash & Sed - Display Unix Directory Structure as a Tree

Recently, I wanted to display a directory structure as a tree. However, I did not want to do programming in a high level language like Java or Ruby and parse the file structure etc. I wanted to use something like Bash.

So, here it goes:
find . -name '*' | sed -e 's/^/|-/' -e 's/[^-][^\/]*\//|   /g' -e 's/|   \([A-Za-z0-9_.]\)/|   +--\1/'

The output of this command will be like this:
|   +--a.out
|   +--arraydecl.c
|   +--coverage
|   |   +--a.out
|   |   +--cov.c
|   +--interposn
|   |   +--cmain.c
|   |   +--cparts.c
|   |   +--cparts.o
|   |
|   |   +--main
|   |   +--main.c
|   |   +--main.o
|   |   +--withso.c
|   +--IPC
|   |   +--pipe.c
|   |   +--pipe_impl.c
|   +--KandR
|   |   +--detab.c
|   |   +--entab.c
|   |   +--Plan.txt
|   |   +--Plan.txt1
|   +--notbitand.c
|   +--ptrjoin.c
|   +--telewords.c

Now to explain this:
First use the find command:
find . -name '*'
This displays the name of all the files and directories under the current directory. Remember that using find would display the relative path from the current directory and does not follow symbolic links, by default.

Next, we need to split the sed command to understand it better.
-e 's/^/|-/'
This replaces the beginning of every line with the characters |-.

Next we have a more complex looking sed expression.
-e 's/[^-][^\/]*\//|   /g'
This replaces any element in the path to the file except the basename with three spaces.

Finally, the expression
-e 's/|   \([A-Za-z0-9_.]\)/|   +--\1/
This replaces the two spaces followed by an alphabet/number/underscore/dot with a "+--" followed by the same alphabet/number/underscore/dot. This gives the "+--" in the structure.