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:
- Read a list of lines from standard input (stdin)
- Narrow candidates as you type, using substring and fuzzy matching
- 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
--previewwhile 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 --bashintegration 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), andALT-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
--previewa 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 inFZF_DEFAULT_OPTSandFZF_DEFAULT_COMMANDto 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 withTAB--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.