This post is part of an educational series on building a shell script to graphically display the structure of a directory.
- We broke down Dem Pilafian’s one line command to display a tree of a directory
- We broke down Dem Pilafian’s script that uses the one line command
Directory and Script Setup
- I would like to see the files in my directory, or, at least, have an option that allows me to see the files.
- I can’t see hidden directories (directories starting with a
- I would like a clearer presentation of the structure.
- If a non-existant directory is given as an argument, it should tell us that it does not exist and exit.
Seeing the Files
This should be easy. Let’s first see how the
ls -R command looks like:
[ahmed@amayem .git]$ ls -R .: branches config description HEAD hooks info objects refs test.sh ./branches: ./hooks: applypatch-msg.sample post-update.sample pre-commit.sample pre-rebase.sample commit-msg.sample pre-applypatch.sample prepare-commit-msg.sample update.sample ./info: exclude ./objects: info pack ./objects/info: ./objects/pack: ./refs: heads tags ./refs/heads: ./refs/tags:
We notice a small problem, the directories are listed more than once. Once inside the parent directory, and once when its contents are listed:
branches config description HEAD hooks info objects refs test.sh ./branches:
We have two
branches listed. Let’s differentiate the contents of each directory using the
-p, --indicator-style=slash append / indicator to directories
This will add a
/ to the directories:
[ahmed@amayem .git]$ ls -Rp .: branches/ config description HEAD hooks/ info/ objects/ refs/ test.sh ./branches: ./hooks: applypatch-msg.sample post-update.sample pre-commit.sample pre-rebase.sample commit-msg.sample pre-applypatch.sample prepare-commit-msg.sample update.sample ./info: exclude ./objects: info/ pack/ ./objects/info: ./objects/pack: ./refs: heads/ tags/ ./refs/heads: ./refs/tags:
Great, now let’s get rid of the directories as listed in the parent directory. We will use
grep "[^/]$" to capture all the lines that don’t end with a
[ahmed@amayem .git]$ ls -RF | grep "[^/]$" .: config description HEAD test.sh* ./branches: ./hooks: applypatch-msg.sample* commit-msg.sample* post-update.sample* pre-applypatch.sample* pre-commit.sample* prepare-commit-msg.sample* pre-rebase.sample* update.sample* ./info: exclude ./objects: ./objects/info: ./objects/pack: ./refs: ./refs/heads: ./refs/tags:
I would like to use
sed now to add the beginning dashes, but I have a problem. How do I figure out how many dashes to put before a file name? We figured out how many dashes to put before a directory based on the number of slashes,
/ in its pathname, e.g. I can replace
./objects/info: with two dashes. After some digging around I found this command:
[ahmed@amayem .git]$ ls -1 */* hooks/applypatch-msg.sample hooks/commit-msg.sample hooks/post-update.sample hooks/pre-applypatch.sample hooks/pre-commit.sample hooks/prepare-commit-msg.sample hooks/pre-rebase.sample hooks/update.sample info/exclude objects/info: objects/pack: refs/heads: refs/tags:
I got some of the files with their relative paths, however this command is not recursive. That means that to get the files three levels down I need to run
ls -1 */*/*. It also has the obvious problem of not displaying the files and directories above the requested depth.
During my digging around I found out I could use the nifty command
[ahmed@amayem .git]$ find . ./branches ./hooks ./hooks/prepare-commit-msg.sample ./hooks/pre-applypatch.sample ./hooks/pre-rebase.sample ./hooks/applypatch-msg.sample ./hooks/commit-msg.sample ./hooks/post-update.sample ./hooks/pre-commit.sample ./hooks/update.sample ./info ./info/exclude ./config ./test.sh ./description ./refs ./refs/tags ./refs/heads ./objects ./objects/info ./objects/pack ./HEAD
That’s great. Now we can get rid of the the
grep ":$" | sed -e 's/:$//' portion of the original command.
[ahmed@amayem .git]$ find | sed -e 's/[^-][^/]*//--/g' -e 's/^/ /' -e 's/-/|/' . |-branches |-hooks |---prepare-commit-msg.sample |---pre-applypatch.sample |---pre-rebase.sample |---applypatch-msg.sample |---commit-msg.sample |---post-update.sample |---pre-commit.sample |---update.sample |-info |---exclude |-config |-test.sh |-description |-refs |---tags |---heads |-objects |---info |---pack |-HEAD
That’s much better. However, I think we can do better. With this output I can’t tell whether
config for example is a directory or a file. It does not seem that
find has the ability to put a slash,
/, after directories like
ls. I also like the options that
ls has, because it would be more compatible with the
tree command that we are designing, in that it is a listing of contents, while as the
find command searches for files.
Let’s move on to making a larger script that does what I want in the next post
We don’t have to add the
-1 flag to print everything out in a list, because when
ls output is piped it is automatically printed as a list:
-1 (The numeric digit ``one''.) Force output to be one entry per line. This is the default when output is not to a terminal.