don’t let cd slow you down, cd wrappers: wcd, commacd

05 Apr 2011

Using a console interface to manage a computer has its disadvantages, some of them are specially visible when dealing with multiple files at the same time (moving/renaming/copying), typing long and crypted commands/options or moving around. On this entry I’ll talk about the last one.

The default cd behaviour on bash to change directories is quite strict, it requires to write full/relative paths and doesn’t recognize fuzzy search or any more complicated way of finding directories, some people use many aliases to workaround these issues, others change its default shell or use third party tools, eg: fasd, bd, fzf, etc to create a semi automatic way of moving faster.

I prefer everything as minimalist and automatic as possible, so after reviewing lots of custom scripts, alternative shells and third party hacks, I think I’ve something good enough to write about and use on a daily basis.

All start by enabling semi hidden bash options for autocorrecting and autocompleting directories:

$ head ~/.bashrc
# http://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
if [ "${BASH_VERSINFO}" -ge "4" ]; then
    shopt -s autocd cdspell dirspell
fi

Now, bash can autocomplete 1/2/3/foo in the following scenarios:

$ cd 1/2/3/foo
$ cd 1/2/3/ofo
$ 1/2/3/foo

Moving between important directories and parent directories can be optimized by adding some aliases:

$ head ~/.alias.common
alias ..="cd .."
alias ....="cd ../.."
alias important.path="cd important/path"

Now it’s time for some major improvements. An important zsh cd related feature is called pattern recognition, e.g. $ cd s/m/pl will become super/master/plan, that’s sweet, unfortunately bash is unable to recognize such patterns by itself, however with some help it can do it even better.

$ sudo apt-get install wcd
$ head ~/.alias.common
alias cd='. wcd'

wcd is not a binary, it’s a wrapper script around wcd.exec (available on the wcd package):

Once installed and configured, $ cd s/m/pl will take us to super/master/plan no matter what the current is, wcd works by creating an index file with all available directories and looking at it to find the best approximation.

WARNING: Wcd will require to regenerate the index db every now and then, a cronjob with the following content can help:

0 23 * * *  /usr/local/bin/update-cd
$ cat /usr/local/bin/update-cd
#!/bin/sh
#description: update wcd db if available
#usage: update-cd

if [ -f "$(command -v "wcd")" ] && [ -f "$(command -v "wcd.exec")" ]; then
    mkdir "${HOME}"/.wcd; wcd.exec -GN -j -xf "${HOME}"/.ban.wcd -S "${HOME}"
    [ -f "${HOME}"/.treedata.wcd ] && mv "${HOME}"/.treedata.wcd "${HOME}"/.wcd/
fi

Been able to move to any directory from anywhere is really helpful, however sometimes it’s desirable to move around parents and nearby directories efficiently, that’s where commacd get in. With commacd several aliases (,, ,, and ,,,) are defined which can be used on the following scenarios:

$ , /u/l/b #moving through multiple directories
=> cd /usr/local/bin
$ , d #moving through multiple directories with the same name
=> 1 Desktop
   2 Downloads
   : <type index of the directory to cd into>
~/code/projects/zion/src/module $ ,, #going up till a project directory is found (git/hg/svn based)
=> cd ~/code/projects/zion
~/code/projects/zion/src/module $ ,, pro #going into the first parent directory named pro*
=> cd ~/code/projects
~/code/projects/zion/src/module $ ,, zion matrix #subtituing the current path and going into the result
=> cd ~/code/projects/matrix/src/module
~/code/projects/zion/src/module $ ,,, matrix/tests #going into a sibling directory who has the same parent directory
=> cd ~/code/projects/matrix/tests/

As wcd, commacd is a script who can be downloaded from:

Or to get my personal version:

Upon getting any of them, the script should be used as an alias (due to the nature of the cd built-in), eg, ~/bashrc:

if [ -f "$(command -v "commacd")" ]; then
    . commacd
    alias ,=_commacd_forward
    alias ,,=_commacd_backward
    alias ,,,=_commacd_backward_forward
fi

That’s it, now moving around should feel less archaic, happy cli browsing 😄