My key requirement for a file system layout is:
Obvious right?
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.
Screencast
The Tools
My shell of choice is zsh
, you may prefer bash
or 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 zsh
, and
bash
has fewer. So anything covered here for zsh
should exist in fish
, and
might have an equivalent in bash
. zsh
is 99% compatible with Bash, and thus
has almost no breaking changes from the original Bourne Shell (/bin/sh
), so
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
path and 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 ~/foo
, /foo
, or
$XDG_CONFIG_HOME/foo
, and they'll all work fine.
You can read more about it in the manual here, or in man zshoptions
.
The CDPATH
When you pass cd
a path that isn't an absolute path (starting with /
), or a
relative path (starting with ./
or ../
), 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
setting the CDPATH
variable. Like $PATH
, $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 cd
.
Note that the $CDPATH
variable is different in zsh
and bash
. In bash
you need to include the current directory at the beginning, otherwise cd
will
prefer ~/foo
to ./foo
(which is rarely what you want in my experience). So I
set CDPATH=~
in zsh
, and CDPATH=:~
in bash
.
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
get 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.
History for cd
setopt autopushd autocd pushd_silent pushdignoredups pushdminus
Lots of detail here, but the key ones are
autopushd
and pushd_silent
. autopushd
makes cd
work like pushd
, which
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_silent
suppresses pushd
's annoying habit of printing the path it is
cd
ing 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 cd
ing to, e.g. when
foo
takes you to ~/foo
not ./foo
. This is the ideal balance between
explicit and minimalism in my (infallible) opinion.
Advanced Tab-Complete
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 .,_-
as
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 ~/code
for
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/n
Tab. 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
want).
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 (c/o/n
).
Summary
With all these features you should never need to spend more than a second typing out a path.