Why every developer should be using fzf, the command line fuzzy finder

In the world of software development, efficiency is king. The more productive we can be with our time and mental energy, the more we can focus on solving difficult problems and shipping great features. One way to supercharge your productivity, especially when working at the command line, is to add fzf to your toolkit.

What is fzf?

fzf stands for "fuzzy finder". In official terms, it‘s a "general-purpose command-line fuzzy finder". While that might sound like a mouthful, in practical terms it means that fzf is an interactive tool that lets you rapidly search and select from a list – whether that list is files in a directory, lines in a file, command history, processes, git commits, or anything else.

fzf has garnered a cult following among developers and power users, amassing over 21,000 stars on GitHub at the time of writing. Once you integrate it into your workflow, you‘ll understand why. It‘s fast, flexible, and composable in a way that exemplifies the Unix philosophy.

How fzf works

At its core, fzf is simply an interactive Unix filter. It reads a list of items from standard input (STDIN), launches an interactive finder window, allows you to type a query to filter the list down, and then writes the selected item(s) to standard output (STDOUT).

fzf interactive finder

This might sound straightforward, but the magic is in the execution. fzf supports fuzzy matching of your query against the list. This means as you type, it instantly narrows down the list to items that contain the characters you‘ve typed, even if those characters aren‘t contiguous. It‘s this interactivity and fuzzy matching that make fzf so powerful.

Fuzzy matching in depth

Let‘s dive a bit deeper into fzf‘s fuzzy matching capabilities, as this is really the core of what makes it so useful.

When you type a query into fzf, it doesn‘t require that query to be an exact substring of the matched items. Instead, it scores each item based on how closely it matches the characters you‘ve typed, in the order you‘ve typed them. The closer together and earlier in the string those characters appear, the higher the match score.

fzf fuzzy matching

For example, if you have a file named "my_awesome_project.js" and you search for "awepro", fzf will match that file because it contains all those characters in that order, even though they‘re not contiguous. A search for "map" would prioritize a file named "my_awesome_project.js" over "map_user_data.py", because the characters appear earlier in the string and closer together.

You can also include spaces in your query to search for multiple patterns. So a search for "map user" would match files containing both "map" and "user" in any order.

While fzf doesn‘t support full regular expressions, it does have some special syntax to give you more control:

  • ^ will only match if the pattern is at the start of the line
  • $ will only match if the pattern is at the end of the line
  • (single quote) will force an exact match of the following characters
  • ! will negate the match, excluding items that contain the pattern

fzf matching syntax

All of this adds up to a search experience that‘s fast, intuitive, and forgiving. You don‘t have to remember the exact name of the thing you‘re looking for – as long as you can recall a few key substrings, fzf will often find it for you.

Searching files

While fzf can filter any list of data, probably the most common use case is searching for files. Let‘s look at how fzf enhances this workflow.

Imagine you‘re deep in a complex directory hierarchy and you need to open a specific file. You know part of the filename, but you‘re not sure of its exact location. Traditionally, you might reach for your file explorer, expanding folder after folder, scanning through long lists of files, until you finally find what you‘re looking for.

With fzf, you can bypass that tedious process. Simply type:

vim `fzf`

This will launch fzf, let you fuzzy search for the file you want, and then open it directly in Vim. You can search by just parts of the filename, regardless of which directory it‘s in. fzf will search recursively through the current directory and all its subdirectories.

fzf file searching

You can also pipe the selected files to any other command:

fzf | xargs ls -l

This will let you select multiple files with fzf and then pass them as arguments to the ls -l command to view their details.

Enhancing the search experience

Out of the box, fzf uses the standard find command to search for files. However, you can significantly enhance the search experience by tweaking the FZF_DEFAULT_COMMAND environment variable to use a more powerful search utility.

One excellent option is fd, a simple, fast, and user-friendly alternative to find. Not only is fd faster than find, but it also respects your .gitignore files, skipping files and directories that you‘ve explicitly excluded from your git repository. This can dramatically reduce the noise in your search results.

Here‘s a quick benchmark comparing fd to find when indexing my home directory which contains about 250,000 files:

Command Time
find ~ -type f 12.7s
fd . ~ 810ms

As you can see, fd is over 15x faster! This speed difference is noticeable even on smaller directories, and it‘s especially impactful when you‘re searching through large codebases.

Another great option for powering fzf is ripgrep, a line-oriented search tool that recursively searches your current directory for a regex pattern. Like fd, ripgrep is extremely fast and also respects your .gitignore files.

Setting FZF_DEFAULT_COMMAND to use either of these tools can make a big difference in the usability of fzf:

export FZF_DEFAULT_COMMAND=‘fd --type f‘
# or
export FZF_DEFAULT_COMMAND=‘rg --files‘

Navigating directories

Another area where fzf shines is in navigating between directories. Changing into a deeply nested directory can be a pain point, requiring either a lot of cd commands or some very long and error-prone typing.

With fzf, you can change directories in just a few keystrokes:

cd **<TAB>

Hitting <TAB> after ** will invoke fzf with a list of all subdirectories in the current tree. You can then fuzzy search for the directory you want, no matter how deeply nested it is.

fzf directory navigation

This is such a frequent operation for me that I have it bound to a keyboard shortcut in my shell. Pressing CTRL-G at any point brings up the fzf directory finder, changes into the selected directory, and clears the line – ready for the next command.

Interactive history search

Another powerful use case for fzf is searching through your command history. While most shells provide some form of history search (usually bound to the Ctrl-R shortcut), fzf takes it to the next level.

By piping your command history to fzf, you get the full fuzzy finding power applied to your previously entered commands. You can search for commands by any substring, not just the first characters. And you get the live, interactive filtering that makes fzf so pleasant to use.

fzf command history search

Here‘s a snippet you can add to your shell configuration to set this up (replacing CTRL-R with your preferred keybinding):

# Bind fzf history search to CTRL-R
bind ‘"\C-r": " \C-e\C-u\C-y\ey\C-u`history | fzf`\e\C-e\er\e^"‘

Now, whenever I press Ctrl-R, I get an fzf-powered history search. I can type any part of the command I‘m looking for, and fzf will find it. When I‘ve selected the command I want, I can press Enter to execute it immediately or Tab to bring it back to the prompt for editing before running.

Git integration

As a developer, a huge portion of my command line time is spent interacting with Git. Whether it‘s checking the status of a repo, committing changes, pushing and pulling from remotes, or reviewing diffs and logs, these are tasks I perform countless times a day.

Unsurprisingly, fzf can make a lot of these interactions faster and smoother. Let‘s look at a few examples.

Checking out branches

When I want to switch to a different branch, I can use fzf to fuzzy find the one I want:

git checkout `git branch -a | fzf`

This pipes the list of all local and remote branches to fzf, lets me fuzzy search for the one I want, and then checks it out.

Viewing commit logs

When I need to find a specific commit, I can pipe the output of git log to fzf:

git log --oneline | fzf

This gives me a searchable, interactive view of the commit history. I can easily find a commit by message, author, date, or hash.

Interactive diffs

One of my favorite Git + fzf combinations is using it to stage individual hunks for a commit:

git diff --name-only | fzf --multi --preview ‘git diff --color=always {}‘ | xargs git add

This command does a few things:

  1. git diff --name-only gets the list of files with unstaged changes.
  2. That list is piped to fzf --multi --preview ‘git diff --color=always {}‘, which lets me use fzf to select multiple files (with --multi) while seeing a live preview of the diff for each file (with --preview).
  3. The selected files are passed to git add, staging them for the next commit.

This is an incredibly powerful workflow, allowing me to easily stage specific changes across multiple files, all without leaving the terminal.

Conclusion

We‘ve only scratched the surface of what‘s possible with fzf. Its utility is really only limited by your imagination. Any time you find yourself needing to select an item from a list – whether that‘s files, commit hashes, docker containers, or anything else – chances are you can use fzf to make that process faster and more enjoyable.

But fzf isn‘t just about saving keystrokes. It‘s about staying in flow. Every time you have to take your hands off the keyboard, navigate through a GUI, or break your concentration to find something, you‘re pulled out of the zone. By bringing fast, intuitive, and interactive searching to the command line, fzf lets you stay focused on the task at hand.

Moreover, fzf exemplifies the Unix philosophy of small, composable tools that do one thing well. Rather than trying to be an all-in-one solution, fzf focuses on being the best possible fuzzy finder. This allows it to integrate seamlessly with the rest of your command line toolkit, enhancing and accelerating your existing workflows without forcing you into new paradigms.

If you‘re not already using fzf, I highly recommend giving it a try. Once you start fuzzy finding, you‘ll wonder how you ever lived without it. And if you are already an fzf user, I hope this article has given you some new ideas for how to leverage its power.

Happy searching!

Similar Posts