32blogby Studio Mitsu

fzf Complete Guide: Fuzzy Finding That Changes Everything

Install fzf, integrate with your shell, supercharge Ctrl+R and Ctrl+T, connect with Git, and customize it for your workflow.

by omitsu12 min read
On this page

fzf is a command-line fuzzy finder that lets you interactively search files, command history, Git branches, and virtually anything piped through stdin. Install it, enable shell integration, and three key bindings — Ctrl+R, Ctrl+T, Alt+C — will transform how you navigate the terminal.

"Where was that file again?" "What was that command I ran three days ago?" "I have too many Git branches to keep track of."

Searching is one of the biggest time sinks in terminal work. history | grep and find . -name only get you so far.

Enter fzf — a fuzzy finder that lets you interactively search files, command history, Git branches, processes, and virtually anything else. Once you start using it, there's no going back to plain Ctrl+R.

What is fzf?

fzf is an interactive fuzzy finder written in Go by junegunn (also known for vim-plug and fzf.vim). The project has over 68k stars on GitHub and is one of the most popular CLI tools in the ecosystem.

It follows the UNIX philosophy: read lines from stdin, let the user pick one, and write it to stdout. This "take anything, return anything" design makes fzf a universal tool that plugs into any workflow.

Key features:

  • Fuzzy matching: you don't need exact strings — partial keywords narrow down results instantly
  • Blazing fast: v0.70.0 improved filtering performance by 1.3–1.9x. Tens of thousands of files are no problem
  • Shell integration: supercharges Ctrl+R (history search), Ctrl+T (file path insertion), and Alt+C (directory jump)
  • Preview: see file contents or Git diffs in real time before selecting
  • Pipes into anything: filter output from find, git, ps, docker — any command that produces lines

Installation and setup

If you're using WSL, follow the Linux instructions above.

For PowerShell:

powershell
winget install junegunn.fzf

Shell integration

fzf 0.48+ greatly simplified shell integration. Add the following to your shell config:

For bash (~/.bashrc):

bash
eval "$(fzf --bash)"

For zsh (~/.zshrc):

bash
source <(fzf --zsh)

Apply the changes:

bash
source ~/.bashrc  # for bash
source ~/.zshrc   # for zsh

Basic usage

Fuzzy search files

Run fzf with no arguments to list all files under the current directory and interactively narrow them down.

bash
fzf

Any file whose path contains the characters you type will remain as a candidate. You don't need an exact match — typing cnf matches config, conf.yaml, nginx.conf, and more.

Pipe input into fzf

This is where fzf really shines.

bash
# Search only JavaScript files
find . -name "*.js" | fzf

# Search environment variables
env | fzf

# Search recent commands
history | fzf

Select multiple items

The -m (--multi) flag enables multi-select with Tab.

bash
# Select multiple files and delete them
rm $(fzf -m)

Preview files while selecting

The --preview option displays file contents alongside the candidate list. If you have bat installed, you get syntax highlighting for free.

bash
# Preview with cat
fzf --preview 'cat {}'

# Preview with bat (syntax-highlighted)
fzf --preview 'bat --color=always {}'

Start with an initial query

Use -q to pre-fill the search field.

bash
fzf -q "test"

Pass the result to another command

Use $(fzf) to feed the selected file into any command.

bash
# Open the selected file in vim
vim $(fzf)

# Open the selected file in VS Code
code $(fzf)

Shell integration

Shell integration is fzf's killer feature. Learn three key bindings and your terminal experience will transform.

Ctrl+R — fuzzy search command history

fzf replaces the shell's built-in Ctrl+R (reverse-i-search):

  • Standard Ctrl+R: steps through history one entry at a time
  • fzf's Ctrl+R: fuzzy-searches the entire history and finds what you need instantly
bash
# How to use: press Ctrl+R at the prompt
# → fzf opens with your full command history
# → type keywords to narrow down
# → press Enter to insert the selected command

That docker-compose command from three days ago? Just type dock and it's right there.

Ctrl+T — insert a file path

Press Ctrl+T while typing a command to search for a file and insert its path.

bash
# How to use:
# type "vim " then press Ctrl+T
# → fzf opens, you search and select a file
# → "vim path/to/selected/file" appears on your command line

Far faster than Tab-completing through deep directory trees.

Alt+C — jump to a directory

Press Alt+C to fuzzy-search directories and cd into the selected one.

bash
# How to use: press Alt+C
# → fzf displays a directory listing
# → select one and cd runs automatically

No more cd + Tab + Tab + Tab to drill down into nested folders.

Customize the default commands

You can control what fzf lists by setting environment variables. fd (a fast, Rust-based find alternative) automatically respects .gitignore and runs much faster.

bash
# If fd is installed (add to .bashrc / .zshrc)
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_ALT_C_COMMAND='fd --type d --hidden --follow --exclude .git'

See Modern Rust CLI Tools for more on fd.

Real-world use cases

fzf's real power comes from combining it with other commands. Here are patterns you can use right away.

Supercharge Git operations

Git gets harder to navigate as branches and commits pile up. fzf makes it effortless.

bash
# Fuzzy-search and switch branches
git checkout $(git branch --all | fzf)

# Fuzzy-search commits (with diff preview)
git log --oneline | fzf --preview 'git show {+1}'

# Fuzzy-search changed files (with diff preview)
git diff --name-only | fzf --preview 'git diff {}'

Find and kill processes

Hunting down a runaway process is a one-liner with fzf.

bash
# Search for a process and kill it
kill $(ps aux | fzf | awk '{print $2}')

# Force kill
kill -9 $(ps aux | fzf | awk '{print $2}')

Combine ripgrep results with fzf to search your codebase and preview matches in context. Perfect for finding TODOs or error patterns.

bash
# Search for TODO comments with preview
rg --line-number "TODO" | fzf --delimiter ':' --preview 'bat --color=always --highlight-line {2} {1}'

Docker container operations

Select a running container and drop into its shell.

bash
# Pick a container and start bash
docker exec -it $(docker ps --format '{{.Names}}' | fzf) bash

# View container logs
docker logs --tail 100 $(docker ps --format '{{.Names}}' | fzf)

SSH host selection

Pick a host from your ~/.ssh/config and connect.

bash
# Fuzzy-search SSH hosts
ssh $(grep "^Host " ~/.ssh/config | awk '{print $2}' | fzf)

Customization

Default options

The FZF_DEFAULT_OPTS environment variable controls fzf's appearance and behavior.

bash
# Add to .bashrc / .zshrc
export FZF_DEFAULT_OPTS='
  --height 40%
  --layout=reverse
  --border
  --info=inline
  --margin=1
  --padding=1
'
  • --height 40%: use only 40% of the screen (no fullscreen takeover)
  • --layout=reverse: show candidates top-to-bottom (default is bottom-up)
  • --border: draw a border around the finder
  • --info=inline: show match count on the prompt line

Color scheme

Customize colors with the --color option.

bash
export FZF_DEFAULT_OPTS='
  --height 40%
  --layout=reverse
  --border
  --color=fg:#c0caf5,bg:#1a1b26,hl:#bb9af7
  --color=fg+:#c0caf5,bg+:#292e42,hl+:#7dcfff
  --color=info:#7aa2f7,prompt:#7dcfff,pointer:#ff007c
  --color=marker:#9ece6a,spinner:#9ece6a,header:#9ece6a
'

Key binding customization

The --bind option lets you add custom key bindings inside fzf.

bash
export FZF_DEFAULT_OPTS='
  --height 40%
  --layout=reverse
  --border
  --bind "ctrl-y:execute-silent(echo {} | xclip -selection clipboard)"
  --bind "ctrl-e:execute(vim {})"
'
  • ctrl-y: copy the current item to the clipboard
  • ctrl-e: open the current file in vim

Shell functions for common patterns

Wrap frequently used patterns in shell functions. Add these to your dotfiles (~/.bashrc or ~/.zshrc).

bash
# Pick a file with preview and open in vim
fv() {
  local file
  file=$(fzf --preview 'bat --color=always {}' --height 80%)
  [ -n "$file" ] && vim "$file"
}

# Switch Git branches with fzf
fb() {
  local branch
  branch=$(git branch --all | fzf --height 40% | sed 's/^[* ]*//' | sed 's|remotes/origin/||')
  [ -n "$branch" ] && git checkout "$branch"
}

# cd into a directory with preview
fd_cd() {
  local dir
  dir=$(find . -type d 2>/dev/null | fzf --preview 'ls -la {}' --height 60%)
  [ -n "$dir" ] && cd "$dir"
}

Advanced techniques

Search syntax modifiers

fzf defaults to fuzzy matching, but you can control search precision with prefixes and suffixes.

SyntaxMeaningExample
keywordFuzzy matchcnf → config, nginx.conf
'exactExact match (prefix ')'config → config only
^prefixPrefix match^src → src/...
suffix$Suffix match.json$ → *.json
!negationExclude!test → excludes anything with test
a | bOR\.js$ | \.ts$ → JS or TS files

Combine them with spaces for AND logic.

bash
# .ts files under src/ that don't contain "test"
fzf -q "^src .ts$ !test"

--header for context information

--header displays guide text at the top of the fzf window. Useful for building interactive tools.

bash
# Show operation hints
fzf --header "Enter: open / Ctrl-D: delete / ESC: cancel" \
    --bind "ctrl-d:execute(rm {})+reload(find . -type f)"

--header-lines=N pins the first N lines of input as a fixed header. Great for preserving column headers from tabular output.

bash
# Keep ps header row visible
ps aux | fzf --header-lines=1

--delimiter and --with-nth for structured data

When working with CSV or colon-delimited data, you can control which fields are displayed vs. searched.

bash
# Show only usernames from /etc/passwd (full line is returned)
cat /etc/passwd | fzf --delimiter ':' --with-nth 1

# Extract just the commit hash from git log
git log --oneline | fzf --with-nth 2.. | awk '{print $1}'

fzf-tmux for popup panels

If you use tmux, the fzf-tmux command runs fzf in a popup pane without taking over your current view.

bash
# Open fzf in the bottom 40% of the terminal
fzf-tmux -d 40%

# Open as a popup window (tmux 3.2+)
fzf-tmux -p 80%,60%

# Git branch switching in a popup
git branch --all | fzf-tmux -p --preview 'git log --oneline -10 {}'

FAQ

What's the difference between fzf and grep?

grep searches file contents for pattern matches and prints matching lines. fzf is an interactive selector — it takes a list of items (file names, command history, process list) and lets you fuzzy-search and pick one. They complement each other: pipe grep results into fzf for the best of both worlds.

Does fzf work on Windows?

Yes. You can install it via winget install junegunn.fzf or choco install fzf. Shell integration (Ctrl+R, Ctrl+T) works in PowerShell and Windows Terminal. For the fullest experience, using WSL is recommended since most fzf examples are Bash/Zsh-based.

Is fzf slow on large repositories?

For most projects, fzf handles tens of thousands of files without noticeable delay. If you work with monorepos containing hundreds of thousands of files, set FZF_DEFAULT_COMMAND to use fd, which respects .gitignore and skips irrelevant directories automatically.

Can I use fzf with Neovim?

Yes. fzf.vim is a companion Vim plugin by the same author. It provides :Files, :Buffers, :Rg, and other commands inside Vim/Neovim. Alternatives like telescope.nvim offer similar functionality with a Lua-native API.

How do I update fzf?

If installed via a package manager: brew upgrade fzf (macOS), sudo apt upgrade fzf (Ubuntu). If installed from the Git clone method, run cd ~/.fzf && git pull && ./install. Check the latest version at the releases page.

Can I use fzf without shell integration?

Absolutely. fzf works standalone — just pipe any list into it: cat urls.txt | fzf. Shell integration (Ctrl+R/Ctrl+T/Alt+C) is optional but highly recommended for daily productivity.

What is the --walker option in fzf 0.48+?

Starting from v0.48, fzf includes a built-in file walker that replaces the need for external find commands. You can configure it with --walker=file,dir,follow,hidden and --walker-skip=.git,node_modules to control traversal behavior.

Wrapping Up

fzf takes the simple concept of fuzzy matching and, through the UNIX pipe philosophy, turns it into a universal tool.

Here's all you need to get started:

  1. Install fzf and enable shell integration (add eval "$(fzf --bash)" to .bashrc)
  2. Start using Ctrl+R / Ctrl+T / Alt+C (immediate productivity boost)
  3. Install fd and bat, then set them as defaults (faster search, better previews)

These three steps give you the bulk of fzf's benefits. As you get comfortable, add Git integrations and shell functions to build a workflow that's uniquely yours.