"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).
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 ~/.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"
}
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.
Related articles:
- grep and ripgrep Guide — ripgrep + fzf integration details
- tmux Guide — development environment to pair with fzf
- Modern Rust CLI Tools — fd, bat, eza, and more
- CLI Toolkit Map — the big picture of CLI tools