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), andAlt+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:
winget install junegunn.fzf
Shell integration
fzf 0.48+ greatly simplified shell integration. Add the following to your shell config:
For bash (~/.bashrc):
eval "$(fzf --bash)"
For zsh (~/.zshrc):
source <(fzf --zsh)
Apply the changes:
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.
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.
# 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.
# 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.
# 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.
fzf -q "test"
Pass the result to another command
Use $(fzf) to feed the selected file into any command.
# 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
# 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.
# 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.
# 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.
# 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.
# 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.
# Search for a process and kill it
kill $(ps aux | fzf | awk '{print $2}')
# Force kill
kill -9 $(ps aux | fzf | awk '{print $2}')
ripgrep + fzf for code search
Combine ripgrep results with fzf to search your codebase and preview matches in context. Perfect for finding TODOs or error patterns.
# 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.
# 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.
# 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.
# 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.
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.
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 clipboardctrl-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).
# 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.
| Syntax | Meaning | Example |
|---|---|---|
keyword | Fuzzy match | cnf → config, nginx.conf |
'exact | Exact match (prefix ') | 'config → config only |
^prefix | Prefix match | ^src → src/... |
suffix$ | Suffix match | .json$ → *.json |
!negation | Exclude | !test → excludes anything with test |
a | b | OR | \.js$ | \.ts$ → JS or TS files |
Combine them with spaces for AND logic.
# .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.
# 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.
# 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.
# 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.
# 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 {}'
Related Articles
- grep and ripgrep Guide — ripgrep + fzf integration details
- tmux Guide — development environment to pair with fzf
- The Complete Guide to Linux Aliases — combine fzf with shell functions and aliases
- Modern Rust CLI Tools — fd, bat, eza, and more
- CLI Toolkit Map — the big picture of CLI tools
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:
- Install fzf and enable shell integration (add
eval "$(fzf --bash)"to .bashrc) - Start using Ctrl+R / Ctrl+T / Alt+C (immediate productivity boost)
- 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.