(Note: knowledge of shell and command-line basics is a prerequisite to understanding this article.)
Introduction
Navigating to deeply nested directories (also called “folders”) is one of the
major inconveniences in personal computing; one of those things that’s annoying
in both command-line and GUI workflows, and that seems to pop up as a
focus-disruptor at annoying times in the midst of other tasks. I wrote gd
to
make switching to deep directories (and between distant directories) easy, and
it’s done a wonderful job of that task since I authored the script around 2
years ago.
gd
stands for “goto directory”; the name is a play on cd
(“change
directory”), the classic directory-navigation command that gd
is implemented
on top of.
Mechanics
gd
is built on the idea of fuzzy-finding, a term for a kind of approximate
(i.e. “fuzzy”) text-matching where a search query can match results that are
somehow similar to the query, instead of just results that mirror the query
exactly. What “similar” means depends on the fuzzy-matching algorithm, but in
the case of gd
, which uses the superb fzf
command-line fuzzy-finder as its UI, it means that one or more fragments of the
query text are found within the candidate in any order. For instance, the phrase
“fuzzy find” would fuzzy-match the phrase “fizz buzz”, because the “f” and “zz”
fragments from “fuzzy” appear in “fizz”, the “fi” fragment from “find” appears
in “fizz”, the “uzz” fragment from “fuzzy” appears in “buzz”, and so on.
"fuzzy find"
---------------------
"f u z z y f i n d"
| | | | | |
"f"| "zz" "fi"
| | | |
| "uzz" |
| | _____|
| |__|______
| | |
"fi"_____| "uzz"
| |
"f i z z b u z z"
--------------------
"fizz buzz"
The words “apple” and “box” would not fuzzy-match each other, because there are no such resonances between them.
The idea of gd
is to use this fuzzy-matching strategy to search through a list
of your commonly-visited directories. This reduces the amount of typing that you
need to do to specify your desired directory, because as soon as you type out
enough of it to distinguish it from the other directories in the list, it’ll
float to the top of the pile as one of the best matches. Courtesy of
fzf
, the top matches are presented to you
as you type, meaning that you can jump to your destination as soon as it’s
available.
The directory list (called an “index” in the source code) is kept in a hidden
file in the user’s home directory, called .gd_idx
. This file is generated by a
recursive directory listing (ls -R
) command, where the top-level directories
are passed in read from a list of preferred directories that the user keeps in
their copy of the script’s source code (the list can be left empty if the user
wants to include all of their directories in the index, though this may result
in a noticeable slow-down on some systems). Invoke the program with the -u
option to regenerate the directory listing (e.g. gd -u
), something I’d
recommend making a cron job for if you use
gd
a lot.
With the design now fully explained, here’s the full source code of gd
:
#!/usr/bin/env bash
# gd: goto directory
# dependencies: fzf
# Note: for this command to work properly, you must prepend it with "."
# the user is recommended to add the alias gd=". gd" to their
# shell rc file
[ -f ~/.gd_idx ] || set -- -u
case "$1" in
-u)
###
# Replace the following example arguments to `ls -R` with
# the subdirectories of your home directory that you want
# `gd` to include. Make sure to include "\" characters at
# the end of each line.
###
cd; ls -R \
downloads desktop .config .emacs.d \
scripts foo bar baz \
| grep :$ | sed 's/:$//' > .gd_idx
echo 'index updated'
cd "$OLDPWD" ;;
*)
cd ~/"$(fzf < ~/.gd_idx)" ;;
esac
Installation
You can install gd
from source via the git
repository. The following shell
commands run through an installation:
git clone https://github.com/bartlebooth-factorial/gd
cd gd
mkdir ~/scripts
cp gd.sh ~/scripts/gd
chmod +x ~/scripts/gd
# Assuming your shell is zsh:
echo 'export PATH=$PATH:$HOME/scripts' >> ~/.zshrc
echo "alias gd='. gd'" >> ~/.zshrc
source ~/.zshrc
(If you’re using a shell other than zsh
, then just replace ~/.zshrc
with
your shell’s rc file, for instance ~/.bashrc
if you’re using bash
.)
Now just edit ~/scripts/gd
to write in the list of directories that you want
to be included in the index, and you’re ready to go!