Software! Math! Data! The blog of R. Sean Bowman
The blog of R. Sean Bowman
August 18 2015

I’ve been spending lots of time at the command line lately and thought I’d share a couple of useful bash aliases and functions that I use every day. Some are based on the awesome utilities fasd and fzf. The former provides tools to manage a database of frequently/recently used files and directories. (That description is not very user friendly: it’s easier than it sounds.) The latter is a curses based tool that selects one line of text from a bunch using interactive fuzzy matching.

Both of these tools are very cool and unixy in that they provide lots of opportunities for composition into more complicated commands. There are lots of examples online, and I certainly can’t claim the ones below as my own. At the very least, I’ve gotten lots of inspiration from this blog post on navigating the filesystem, a hackernews post, and several blog posts on command line directory traversal and shell aliases more generally. There are probably lots of other things I’ve seen and stolen that I have already forgotten.

Jumping around the filesystem

First, I use a version of the fasd_cd function included with fasd to jump to often used directories. I added some command line completion, which seems redundant here, or just dumb, but I do sometimes find myself pressing tab out of habit and I like for completion to work.

j() {
    local dir="$(fasd -ld "$@")"
    [[ -d "$dir" ]] && pushd "$dir"
}
complete -d j

The next function uses fasd to sort directories by frecency, fzf to drill down, and then pushd to change to the selected directory. It replaces your home directory name with ~ just for aesthetics. (Frecency is a combination of frequency and recency used by fasd and other tools like it; see frecency at Wikipedia.) It is useful to have set -o pipefail somewhere in your shell startup (see here for details), otherwise pushd happens even if you exit with ctrl-c and clutters up the directory stack.

jj() {
    local dir
    dir=$(fasd -Rdl |\
        sed "s:$HOME:~:" |\
        fzf --no-sort +m -q "$*" |\
        sed "s:~:$HOME:")\
    && pushd "$dir"
}
complete -d jj

Finally, two functions substantially taken from the FZF documentation. The first changes to a directory chosen using fzf; the difference between this and jj above is that jj populates the list with your most often/most recently used directories at the bottom, whereas this uses find to list all subdirectories of a given (or the current) directory. The second function changes to the directory containing a selected file. I don’t use it often, but it’s cool when you need it.

jd() {
    local dir
    dir=$(find ${1:-*} -path '*/\.*'\
        -prune -o -type d\
        -print 2> /dev/null | fzf +m)
    [ -d "$dir" ] && pushd "$dir"
}
complete -d jd

jf() {
    local file
    local dir
    file=$(fzf +m -q "$1")\
        && dir=$(dirname "$file")
    [ -d "$dir" ] && pushd "$dir"
}
complete -f jf

Viewing files, pretty and quick

The command les is like less, but with syntax highlighting for file types supported by pygmentize. My version of pygmentize is not great at guessing sometimes, so I have the extra line to determine which lexer to use. The shorter command v either shows a file (if the match is unique) or allows you to choose interactively using fzf. It shows the file with les. Sometimes even when given an actual path to an existing file, fzf still returns lots of matches. In this case, I usually just want to see the file itself, so I short circuit the fzf stuff. Finally, there is some simple bash command line completion for both of these.

les() {
    ftype=$(pygmentize -N "$1")
    pygmentize -l "$ftype"\
      -f terminal "$1" |\
        less -R
}

v() {
    local file
    if [[ -e "$1" ]]; then
        les "$1"
    else
        file=$(fzf --query="$1"\
          --select-1 --exit-0)
        [ -n "$file" ] && les "$file"
    fi
}

complete -f les
complete -f v

Here’s one I just love – so simple and so useful. I must admit that I got so used to typing find . -name blahblah that it’s been hard to switch. Also, I must have been completely dense not to come up with something like this earlier… anyhow, ff for “find files”:

function ff () { find . -name "$@" ; }

How much stuff do I have and where is it?

Nothing to do with fzf, fasd, or anything else, but I love this one. It shows space usage of a directory in human readable form, sorted from small to large, one or more directories deep. By default it shows the space usage of the current directory one level deep; you can show usage of a different directory by passing it as the first argument and show more levels by passing a number as the second. For example, usage . 2 shows space usage of the current directory 2 directories deep. This requires a version of sort that knows of the -h flag, compare human readable numbers. (Possibly fancy du and sed, too; I’m blessed by not having to deal with weird versions of these commands!)

usage() {
    du -h --max-depth="${2:-1}"\
      "${1:-.}" |\
        sort -h |\
        sed "s:\./::" |\
        sed "s:$HOME:~:"
}

complete -d usage

Other little command line goodies I love

FZF comes with some killer features out of the box that save time and make me smile:

  • Search history with ctrl-r, like you’re used to, but way nicer with interactive fuzzy matching. I’m probably not doing it justice with this description, but it’s an awesome feature and saves tons of time and frustration.

  • Pressing tab after kill lets you select a process to send a signal to, again with the interactive fuzzy matching. Completely replaces my old workflow, which used to involve lots of annoying uses of ps waux | grep whatever. Makes me very happy.

  • Anywhere on the command line, ctrl-t opens a list of files and directories and pastes the selected one at the spot you were. For example I can say nano <ctrl-t>, type a few letters of a file deep in some subdirectory (say, dfoo) press enter, and have nano a/b/c/d/foo.txt waiting for me to press enter to edit. Handy when you need it.

Approx. 1033 words, plus code