Quickly navigate your filesystem from the command-line

Published on 16 August 2013

Update (6-9-2013) This code is now also available as the "jump" plugin in oh-my-zsh.

Update (18-8-2013): Thanks to the many useful suggestions in the discussion on Hacker News, I have added (1) quotes to the code, (2) a section about tab completion, and (3) a note for Mac OS X users.

Like many others, I spend most of my day behind a computer. In order make the most of it (and to keep my body from complaining too much), I try to maintain an optimized setup. For example, I code in Vim, browse with Vimperator, and move windows around in i3. Another common task is filesystem navigation. I prefer to use the command-line for this, but typing cd ~/some/very/deep/often-used/directory over and over again does become cumbersome.

Automated tools like autojump, z, and fasd address this problem by offering shortcuts to the directories you often go to. Personally, I prefer a more manual solution, which I would like to share with you. I have noticed quite an increase in efficiency with this, and perhaps you will too.

Jumping with symbolic links

Under the hood this manual solution comes down to storing symbolic links in a hidden directory (e.g., ~/.marks). There are four shell functions jump, mark, unmark, and marks, and they look like this:

export MARKPATH=$HOME/.marks
function jump { 
    cd -P "$MARKPATH/$1" 2>/dev/null || echo "No such mark: $1"
}
function mark { 
    mkdir -p "$MARKPATH"; ln -s "$(pwd)" "$MARKPATH/$1"
}
function unmark { 
    rm -i "$MARKPATH/$1"
}
function marks {
    ls -l "$MARKPATH" | sed 's/  / /g' | cut -d' ' -f9- | sed 's/ -/\t-/g' && echo
}

Put this in your .zshrc or .bashrc and you're ready to jump (Mac OS X users need a slightly different version of the marks function; see below). I have also turned this into a plugin for oh-my-zsh called jump. To add a new bookmark, cd into the directory and mark it with a name to your liking:

$ cd ~/some/very/deep/often-used/directory
$ mark deep

This adds a symbolic link named deep to the directory ~/.marks. To jump to this directory, type the following from any place in the filesystem:

$ jump deep

To remove the bookmark (i.e., the symbolic link), type:

$ unmark deep

You can view all marks by typing:

$ marks

deep    -> /home/johndoe/some/very/deep/often-used/directory
foo     -> /usr/bin/foo/bar

That's all there is to it!

Adding tab completion

In order to add tab completion for the jump and unmark functions, add the following code to your .zshrc (thanks to tiziano88):

function _completemarks {
  reply=($(ls $MARKPATH))
}

compctl -K _completemarks jump
compctl -K _completemarks unmark

or the following to your .bashrc (thanks to microcolonel):

_completemarks() {
  local curw=${COMP_WORDS[COMP_CWORD]}
  local wordlist=$(find $MARKPATH -type l -printf "%f\n")
  COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$curw"))
  return 0
}

complete -F _completemarks jump unmark

If you now type jump or unmark and then press TAB, you see a list of available marks. Neat!

Note for Mac OS X users

As pointed out by guygurari, Mac OS X users need a slightly different version of the marks function:

function marks {
    \ls -l "$MARKPATH" | tail -n +2 | sed 's/  / /g' | cut -d' ' -f9- | awk -F ' -> ' '{printf "%-10s -> %s\n", $1, $2}'
}

If you like what I had to say then you may want to follow me on Twitter.