Command-line Fuzzy Finder (FZF) is a gem of a command-line tool.

It is like the old 4dos SELECT command that could prompt a user to choose a file, times a thousand.  It works with any stream of items you pipe in, presenting an interactive UI that lets a user filter and select items for a given action.

Ever since I got my dotfiles repo going, I've been looking for ways to speed up even small tasks I find myself repeating, especially on the command-line.

🎺 Enter FZF 🎺

hat tip

Much of what follows was derived or sourced from work already done by the community around FZF,  though I've tweaked and tuned a few things for my taste.  I've tried to include references to the origin for each of the following items both here and in my commit comments as I added them to my dotfiles repo.  We are all standing on the shoulders of giants after all.

Show me, Show me, Show me how you do that trick!

Fuzzy Change Directory - fcd

fcd: Quickly diving to a leaf directory

Change to any directory on disk with a few keystrokes.  If you do not pass arguments to fcd, it defaults to a search root of the user home directory.  Pass a base dir to limit scope.

Press enter to change directory.

Click to show fcd source
function -d "Fuzzy change directory"  fcd
    if set -q argv[1]
        set searchdir $argv[1]
    else
        set searchdir $HOME
    end

    # https://github.com/fish-shell/fish-shell/issues/1362
    set -l tmpfile (mktemp)
    find $searchdir \( ! -regex '.*/\..*' \) ! -name __pycache__ -type d | fzf > $tmpfile
    set -l destdir (cat $tmpfile)
    rm -f $tmpfile

    if test -z "$destdir"
        return 1
    end

    cd $destdir
end

from: https://gist.github.com/rumpelsepp/b1b416f52d6790de1aee

fkill - fuzzy kill

fkill: Killing DNSResponder and DNSResponderHelper just prior to dns cache flush on OS X

Kill one or more processes.  Multi-select with tab and then press enter to kill several.

Click to show fkill source
function fkill -d "Fuzzy kill"
    set pid (ps -ef | sed 1d | fzf -m | awk '{print $2}')

    if test -n "$pid"
        echo $pid | xargs kill -9
    end 
end

from: https://gist.github.com/rumpelsepp/b1b416f52d6790de1aee

Deeper down the rabbit hole

The 4 wonderful things about FZF that make it so flexible are:

  1. You can feed anything in to populate a list of choices
  2. As the choices stream in, a user can fuzzy match type ahead to narrow matching items in the list.
  3. A preview window can be configured to show more context around a given choice when it is highlighted.
  4. Once a selection is made, you can trigger an action driven off that selection.
The default matching algorithm in FZF lets you type words or parts of words in any order.

Fish + FZF + Key Bindings

I'm a big fan of the fish shell.  There's a great 3rd party package that provides some useful keybindings and scripts to integrate fish with fzf.  Install that package, then set the environment var FZF_LEGACY_KEYBINDS to 0 and FZF_COMPLETE to 1, and you'll get the following:

  • Tab: use fzf for tab completion
  • Ctrl-F: insert a file path
  • Ctrl-R: search history w/fzf
  • Alt-O: change into subdirectories
  • Ctrl-O: open file using default editor
  • Ctrl-G: open file using system bound app (pdf/img/etc)

Tab: Tab completion - replacement for default shell behavior

Tab complete files or directories, w/search and preview

Tab complete a directory or file name w/preview pane of directory contents.  The  big difference between this and the typical baked in tab completion is that you get a preview pane view of what's in the directory you're selecting (see right half of screen during this interaction).

Source

Ctrl-F: Find a file

Ctrl-F to find a file and insert full path in place.

The search root is based the word by the cursor, so you can hit control-F even with a partial path in place.

Source

Ctrl-R: Search through history

Ctrl-R search history using fuzzy match and multiline preview

Source . . . Added the preview window by setting FZF_REVERSE_ISEARCH_OPTS to --preview-window=up:10 --preview="echo {}" --height 100%

Git + FZF

fstash - fuzzy stash management

Stashing changes is one of those things I do just rarely enough that I never get faster at managing it.   Mostly I stash, then stash pop after a pull from remote and a rebase, but sometimes.... sometimes, something else distracts you and then the stashes pile up.  

fstash provides a minimal interface for managing stashes in a git repo.

Consider the case where we have a repo, and we stash two seperate sets of changes, then need to kill off one of them, and check the other out as a branch:

fstash for managing git stashes: view diff's, delete and checkout as branch

You can bind keys other than enter to custom actions in FZF.   In this case, fstash provides the following:

  • Enter: print the diff of the stash
  • Ctrl-B: checkout the stash as a branch and remove the stash
  • del: drop the stash

Source

fclone - select and clone a github repo

See more about fclone and fhub in detail:  here.

git fco - fuzzy check out branch

source

git fza - fuzzy add multiple files

Filter, select and stage multiple untracked and modified files

source

more fun

  • git frebase: Select a past commit to start a rebase from.  source
  • git ffix - Select a past commit to fixup staged changed to. source
  • git fed - Select untracked/modified files to edit.  source
  • git fedconflicts - Select files in conflict state to edit.  source
  • git fgrep [pattern] [pathspec] - git grep for a pattern in a pathspec (current or former state of repo)  source
side-note:  For insanely fast searching of the current state of large git repo's, check out ripgrep.
  • git fedlog - Choose a file from a commit to edit.  source
  • git freset - Select a prior commi to soft reset to.  source   Undo this with git reset ORIG_HEAD or the appropriate 'HEAD@{1}' ref from the reflog.
  • fhub - Select and browse to a github repo.

Docker

enter - jump inside a running docker container

Let's say you started up the kanboard container some time ago . . .

docker run -d --name kanboard -p 80:80 -t kanboard/kanboard:v1.2.8

. . . and now you need to peek at the inside.

If you're docker savvy, you'll know there are a couple of ways to get in, but even so, there is often some trial and error identifying which shell, if available in a container you are not intimately familar with.   enter to the rescue:

Quickly 'enter' a currently runing docker container with whatever shell is available

Source