fzf: Fuzzy Finder for the Command Line

fzf: Fuzzy Finder for the Command Line

What Is fzf?

Conclusion: fzf is a UNIX filter that reads a list from stdin, lets you fuzzy-search it interactively, and prints the chosen line to stdout. It speeds up every "pick one from a list" task.

fzf (fuzzy finder) is a general-purpose interactive filter for the command line. Its model is simple and comes down to three steps:

  1. Read a list of lines from standard input (stdin)
  2. Narrow candidates as you type, using substring and fuzzy matching
  3. Print the chosen line(s) to standard output (stdout)

Because it reads stdin and writes stdout, fzf composes with any command: find | fzf, ps -ef | fzf, git log | fzf. Any list becomes something you can filter on the spot and select one (or several) lines from.

What you'll learn

  • Speed up daily work with the three key bindings: CTRL-R / CTRL-T / ALT-C
  • Use the extended search syntax (exact, prefix, negation) to refine matches
  • Write practical recipes that preview content with --preview while you choose

Assumptions (target environment)

  • OS: Linux (Ubuntu / RHEL family) or macOS
  • Shell: bash or zsh
  • fzf version: 0.48.0 or later recommended (so the fzf --bash integration flags work)

How Do You Install fzf?

Conclusion: Your distro's package manager (apt / dnf / brew) is the easiest path. For the newest version or full key bindings, use the official install script from GitHub.

Installing via a package manager is the simplest route.

# Ubuntu / Debian
sudo apt install fzf

# Fedora / RHEL family
sudo dnf install fzf

# macOS (Homebrew)
brew install fzf

If the distro package is old, or you want key bindings and completion set up reliably, install straight from the official repository.

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

The install script is interactive: it asks whether to enable key bindings, completion, and to update your shell config. Answering y to everything is fine for most users.

fzf --version
0.55.0 (cabc3c0)

If your version is below 0.48.0, the shell integration below needs the "legacy" method (sourcing a generated file). Installing a newer build is the shortcut.

Enable the Three Key Bindings with Shell Integration

Conclusion: Shell integration enables CTRL-R (history), CTRL-T (insert file), and ALT-C (change directory). On fzf 0.48.0+ it's a single line in your shell config.

Add one line to your shell config file.

# ~/.bashrc (bash)
eval "$(fzf --bash)"
# ~/.zshrc (zsh)
source <(fzf --zsh)

Reload with source ~/.bashrc (or open a new terminal). The three key bindings are now active.

Key Action
CTRL-R Fuzzy-search command history and insert the result
CTRL-T Pick a file/directory under the current path into the command line
ALT-C Pick a subdirectory and cd into it immediately

The biggest win is CTRL-R. Unlike the default CTRL-R (incremental reverse search), you can type half-remembered words in any order and get a scrollable list to choose from.

For fzf below 0.48.0

The fzf --bash flag is unavailable. Instead, source the config file generated at install time (the path depends on your setup).

# Example: when installed via the git clone method
[ -f ~/.fzf.bash ] && source ~/.fzf.bash

Why Is fzf's Search So Fast? The Match Syntax

Conclusion: By default fzf does fuzzy AND-matching across space-separated terms. Add symbols for exact, prefix/suffix, negation, and OR to control filtering precisely.

In the input box, characters match even out of order and non-contiguously (e.g. ako hits awk-oneliners). Symbols switch the match mode.

| Input | Meaning | | ---------- | ------------------------------------------------ | ------------------------------- | | git push | Contains both git and push (AND, fuzzy) | | 'wild | Contains wild as an exact match (no fuzzy) | | ^core | Starts with core | | .md$ | Ends with .md | | !test | Does not contain test (negation) | | ^src | ^app | Starts with src or app (OR) |

# Keep only files ending in .log that do not contain "archive"
find . -type f | fzf --query "'.log$ !archive"

--query (short -q) supplies an initial query, handy when a script should launch fzf already partially filtered.

How Do You Use It in Practice? (Recipes)

Conclusion: The core pattern is embedding $(... | fzf) in command substitution. "Pick then run" tasks (open in vim, kill a process, switch git branch) become one-liners.

Pick a file and open it in vim

vim "$(fzf)"

Pick a process and kill it

kill -9 "$(ps -ef | fzf | awk '{print $2}')"

Pick a git branch and check it out

git checkout "$(git branch --all | fzf | tr -d ' *')"

Pick a directory and move into it (without ALT-C)

cd "$(find . -type d | fzf)"

For multiple selections, add -m (--multi) and mark rows with TAB. All selected lines are printed, newline-separated.

# Stage several files for removal at once
rm -i $(fzf -m)

Preview Content While You Choose

Conclusion: Pass --preview a command that takes the current line as input, and the content shows on the right. The placeholder {} expands to the current candidate.

Peeking at file contents during selection cuts down on wrong picks.

# Show the contents of the selected file
fzf --preview 'cat {}'

# With bat, get a colorized preview (line numbers, syntax highlighting)
fzf --preview 'bat --color=always {}'

{} expands to "the line currently highlighted". Combined with git log, you can review a diff while picking a commit.

git log --oneline | fzf --preview 'git show --color=always {1}'

{1} is the first field of the selected line (the commit hash). You can reference fields inside the preview command.

Common Options and Environment Variables

Conclusion: Shape the look with --height / --layout=reverse / --border. Set defaults once in FZF_DEFAULT_OPTS and FZF_DEFAULT_COMMAND to apply them everywhere.

Frequently used options

  • --height 40%: Show the window in the bottom 40% instead of taking the whole screen
  • --layout=reverse: Input on top, candidates below (reads top-down)
  • --border: Draw a border
  • --multi / -m: Multi-select with TAB
  • --preview 'cmd {}': Preview window

Pin defaults with environment variables

# Add to ~/.bashrc or similar
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'

# Swap the candidate command for fd / ripgrep (respects .gitignore, skips hidden)
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'

FZF_DEFAULT_COMMAND generates candidates when you run fzf with no arguments and for CTRL-T. Using fd or rg --files gives a fast list that honors .gitignore.

If the command set in FZF_DEFAULT_COMMAND (fd / rg, etc.) isn't installed, fzf launches with an empty candidate list. Point it at a real binary name, not a shell alias.

Copy-paste: the first config to add

# ~/.bashrc
eval "$(fzf --bash)"
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'

That alone gives you CTRL-R / CTRL-T / ALT-C plus a clean default layout.

Summary and Next Reading

fzf accelerates the whole "pick one from a list" side of command-line work. Start with CTRL-R history search, then fold --preview and command substitution into your own workflow as you get comfortable.