My key requirement for a file system layout is:
There are a few implications of that basic rule.
- It should be instinctively obvious where any file in the system is (after an initial acclimatisation period).
- There should be minimal typing required to get to any directory
- The system should scale without additional effort.
- Directories used less frequently can be (slightly) harder to get to.
My shell of choice is
zsh, you may prefer
fish. There are some
great features in
zsh to help you get around the system.
Note: As a general rule
fish has at least the same features as
bash has fewer. So anything covered here for
zsh should exist in
might have an equivalent in
zsh is 99% compatible with Bash, and thus
has almost no breaking changes from the original Bourne Shell (
it's a lot easier to use in parallel with other shells.
The autocd option
This is a great
zsh feature. If you type a string into the terminal that
zsh doesn't recognise as a valid command, then it will try to parse it as a file
cd to it.
This means instead of typing
cd foo you can just type
foo (as long as that's
not also a command on your system, if it is just type
foo/). It works for
absolute paths and handles variables as well, so you can type
$XDG_CONFIG_HOME/foo, and they'll all work fine.
You can read more about it in the manual here, or in
When you pass
cd a path that isn't an absolute path (starting with
/), or a
relative path (starting with
cd will assume it's a relative
path starting from the current directory (
$PWD). However you can also tell
cd to look in other directories if it's not in the current directory, by
CDPATH variable. Like
$CDPATH is a colon-separated list
of paths. However whereas
$PATH is the list of paths to check for binaries,
$CDPATH is the list of paths to check for arguments to
Note that the
$CDPATH variable is different in
you need to include the current directory at the beginning, otherwise
./foo (which is rarely what you want in my experience). So I
It's worth emphasizing that the
CDPATH only works for
cd (surprisingly!), so
you won't be able to take advantage of it for other operations like
cp foo ~/tmp. This is one of the reasons I make sparing use of it, and only use it for
~. Adding an extra
~/ is only two extra characters, so it's not the end of
the world. The Tab-Completion shortcuts I'll cover next work everywhere.
Tab-Complete for Paths
This is the killer feature of
zsh for me, and combined with the features above
it really shines. This is easiest to explain with an example. If I want to
~/code/rust/xi-editor, I can just type
c/r/x and then press
Tab. If there is only one possible combination with those paths then
it will directly complete to the full path, if there are multiple it will prompt
you to choose. This means it's suddenly very easy to
cd into deeply nested
directories, and that in each directory it's best to have every subdirectory
start with a different letter.
Run command on chpwd
Allows you to run arbitrary commands whenever you change a directory. I just use
it to run
ls -A every time I change directories, so I always know what's in
the directory I'm in.
More info here.
I set these zsh options:
setopt autopushd autocd pushd_silent pushdignoredups pushdminus
Lots of detail here, but the key ones are
cd work like
keeps a history of the directories you have
cd'd to. You can go back by typing
popd, or better by typing
cd - Tab. This gives you an interactive
menu for which directory you want to go to. See the picture below for more info.
pushd's annoying habit of printing the path it is
cding to. However it doesn't stop the printing of the path that comes from
$CDPATH. What this means specifically (with my
$CDPATH setup) is that it
prints the path whenever it's non-obvious where you're
cding to, e.g. when
foo takes you to
./foo. This is the ideal balance between
explicit and minimalism in my (infallible) opinion.
It's quite complex to get this stuff to work, but you can do some fancy things
with the completion widget. I tell zsh to treat
completion boundaries, but much more advanced completion is possible.
Laying out your file system
Given these features, it's important to lay out your filesystem to take
advantage of them. The first thing is to put everything in your home directory
~/), which is good practice in general.
You then probably want one directory to put your code in, which doesn't share a
first letter with anything else in your home directory. I use
personal stuff, and
~/wrk for work stuff, but you can use anything.
Within those directories you want to keep the pre-tab-complete paths as short as
possible, so to get to
~/code/node you only want to have to type
c/nTab. If you also have
~/code/npm then it won't work, but if
you have a
~/code/node-gyp it will (it completes to the shortest substring in
all possible completions, which in this case is
~/code/node, which is what you
Basically if something you access rarely conflicts with something common, either
change the name to something with a different first letter (that you can
remember), or put it in a subdirectory (e.g.
~/code/other/npm), which can then
be accessed quickly (
With all these features you should never need to spend more than a second typing out a path.