TL;DR: fzf turns a list of candidates into one selected item in a few keystrokes. Once you have the default bindings wired up, you stop arrow-keying through your history and your cd shortcut becomes a fuzzy match.

Before you start

fzf ships as one binary. The shell integration sets up three key bindings at your shell prompt (Ctrl+R, Ctrl+T, Alt+C) and this issue covers each one. fzf has more keystrokes inside the picker TUI itself, but those are a separate topic covered later in this series.

Getting the binary is the easy part. Getting the key bindings wired up is where most people stop — and then they conclude fzf is "just a fuzzy picker" because they never actually used it as a shell tool.

Install via your package manager (the default)

On Ubuntu or Debian, the standard install is:

sudo apt install fzf

The package puts the fzf binary on your PATH. To wire up the key bindings, source the shell integration files that ship with the package. The reliable way to find their paths is:

dpkg -L fzf | grep -E 'key-bindings|completion'

From my experience, on Ubuntu, the key-bindings file is typically under /usr/share/doc/fzf/examples/ and the completion script is under /usr/share/bash-completion/completions/fzf. You need to source both in your ~/.bashrc.

source /usr/share/doc/fzf/examples/key-bindings.bash
source /usr/share/bash-completion/completions/fzf

The exact paths vary by distro and release (Debian, Ubuntu, Fedora, Arch all put them in different places). The dpkg -L discovery step above is the portable version — run it on your machine and source whatever it returns.

On macOS, Homebrew installs fzf and runs the shell integration step in one line: brew install fzf && $(brew --prefix)/opt/fzf/install.

When the package version is older than the docs

Debian or Ubuntu ships fzf in its package repos, but the packaged version may lag the upstream release by a year or more. The support for the README's one-line snippet eval "$(fzf --bash)" — the documented shortcut for loading the integration on bash — was added in recent versions of fzf.

If you have 0.48 or newer, the eval "$(fzf --bash)" (bash) / source <(fzf --zsh) (zsh) snippets from the README work and you can replace the two source lines above with that one line. If you have an older one, the two source lines from the previous section are the paths your distro's package expects you to use.

Install via the fzf repo (the fallback)

If you want a recent fzf without waiting for your distro to catch up, the fzf repo's own install script works on any Linux:

git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
source ~/.bashrc

This is the install method the README documents as the manual alternative to the package-manager path.

What the key bindings do

Once the shell integration is loaded, you have these key bindings available at every shell prompt, according to the docs:

  • Ctrl+T — helps you select a file or directory, and paste the selected file / directory path at your cursor.

  • Ctrl+R — helps you select a command from your shell history, and paste the selected command at your cursor.

  • Alt+C — helps you select a directory and cd into the selected directory.

To verify the integration actually loaded, press Ctrl+R at an empty prompt: a fuzzy list of recent commands should appear. If it does not, your shell rc file was not sourced — open a new terminal, or source ~/.bashrc (or the quivalent for your case).

This issue covers the install and two of the three key bindings in full (Ctrl+R and Ctrl+T). For the third binding, Alt+C, we will quickly check that it behaves as documented.

1. Search shell history

Press Ctrl+R. A fuzzy list of recent commands appears. Type a few letters (git co, git sta, docker) and the list narrows to matching commands. Press Enter to get the highlighted one on your cursor, or arrow keys to move to a different match.

The point of Ctrl+R is not that it is faster than ↑↑↑↑↑↑. It is that you USE your shell history instead of avoiding it. The arrow-key loop is so slow that most people give up at some point and retype the command from scratch. fzf's fuzzy match removes that penalty: you type a fragment, you find the command, you press Enter.

2. Pick a file

Press Ctrl+T. A fuzzy file picker opens, anchored to your current directory. Type a fragment of the path. mod finds src/lib/models/, uv.lock finds sample_project/uv.lock. Pick the file, press Enter, and its path is inserted at your cursor.

The use case: you are writing a command and need to reference a file. Type the start of your command (cat , vim , less ), press Ctrl+T, pick the file, press Enter, and the path lands in your command line. Then press Enter once more to actually run the command with the file. It feels small until you do it twenty times in a coding session.

3. Jump into a directory

Press Alt+C. A fuzzy directory picker opens. Type a few letters of the directory name, pick one, press Enter, and your shell cds into it.

This is the binding most people may under-use. It is enabled alongside the two others and works the way the README documents — but its value compared to zoxide (which is covered in the PDF) is debatable.

What fzf is NOT

fzf is not a search engine. If you want to find every file in a tree that contains the word TODO, run rg TODO (covered in a future issue). fzf is interactive; it is designed to show you a list and let you pick one. Pasting a million-line log into fzf will hang your terminal. rg and fd are the right tools for "find all the things that match a pattern." fzf is the right tool for "I have a short list, help me pick."

fzf is not a replacement for typing paths you know. If you know the path is ~/.config/starship.toml, type it. Ctrl+T is for when you do not know the path, or only remember a fragment.

fzf is not magic. The fuzzy match is substring-based with some smart scoring; it does not understand regex, exact match, or content search. If you need those, you are in rg territory, not fzf territory.

One CLI trick

# Find where your distro's fzf package put the shell integration files
dpkg -L fzf | grep -E 'key-bindings|completion'

If you installed fzf via apt and fzf --bash does not exist on your version, this is the command that tells you which file to source to get the key bindings wired up. Use the paths it returns in the legacy snippet shown in the "Before you start" above.

Want the full toolkit?

The Modern CLI Stack is a free 25-page PDF covering mise, uv, starship, zoxide, fzf, ripgrep, fd, bat, eza, delta, tldr, atuin, lazygit, and broot. Get it on Gumroad here.

Reply

Avatar

or to participate