fzf — Fuzzy Finder with ripgrep
Practical fzf cheatsheet covering key bindings, ripgrep integration, preview windows, and reusable shell functions for fast terminal fuzzy searching.
Code
fzf — Fuzzy Finder with ripgrep Cheatsheet
fzf is an interactive fuzzy finder: it takes a list on stdin, filters it as you
type, and prints what you pick on stdout. Combined with ripgrep (rg) for fast
file and content listing, it turns the terminal into a rapid navigation tool. This
cheatsheet covers the basics, default key bindings, piping rg into fzf, the
preview window, and writing your own fzf-backed shell functions.
Install
# macOS
brew install fzf ripgrep
# Debian / Ubuntu
sudo apt install fzf ripgrep
# Enable key bindings + completion (Homebrew install path)
$(brew --prefix)/opt/fzf/install
After installing, source the shell integration so Ctrl-R/Ctrl-T/Alt-C work:
# bash
eval "$(fzf --bash)"
# zsh
source <(fzf --zsh)
Basics
# Pick a file from the current tree, print the choice
find . -type f | fzf
# Open the selection in your editor
vim "$(fzf)"
# Read selection from a pipe
cat /etc/hosts | fzf
Search syntax inside the fzf prompt (extended-search mode, on by default):
sbtrkt fuzzy match (any char order)
'wild exact match (items containing "wild")
^music prefix match (starts with "music")
.mp3$ suffix match (ends with ".mp3")
!fire inverse match (items NOT containing "fire")
foo bar AND: both terms must match
foo | bar OR: either term matches
Useful flags:
fzf -m # multi-select (TAB / Shift-TAB to toggle)
fzf -i # case-insensitive
fzf +i # case-sensitive
fzf -q 'init' # start with an initial query
fzf --height 40% # render inline instead of full screen
fzf --reverse # prompt at the top, list below
fzf --print-query # also print the typed query, not just the match
Key bindings (shell integration)
Once the shell integration is sourced, these defaults are available at any prompt:
CTRL-R Fuzzy-search command history; Enter pastes it to the prompt
CTRL-T Fuzzy-pick file/dir paths; inserts them at the cursor
ALT-C Fuzzy-pick a directory and cd into it
Bindings inside the fzf window itself:
CTRL-J / CTRL-N / Down move down
CTRL-K / CTRL-P / Up move up
TAB / SHIFT-TAB toggle selection (with -m / multi)
ENTER accept selection
ESC / CTRL-C / CTRL-G cancel
CTRL-/ toggle preview window
SHIFT-Up / SHIFT-Down scroll preview
Customize what Ctrl-T and Alt-C list by exporting commands (here using rg/fd):
# Use ripgrep to feed CTRL-T (files only, includes hidden, skips .git)
export FZF_CTRL_T_COMMAND='rg --files --hidden --glob "!.git/*"'
# Default command used when fzf is run with no input piped in
export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git/*"'
# Sensible global look & feel
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'
Piping ripgrep into fzf
List files with rg, then fuzzy-pick one to open:
rg --files | fzf | xargs -r -o nvim
Live content search — re-run rg on every keystroke using --bind change:reload.
fzf here is just the picker; rg does the matching:
rg --line-number --no-heading --color=always --smart-case '' \
| fzf --ansi
A fully interactive grep where typing reloads rg:
INITIAL=""
rg --line-number --no-heading --color=always --smart-case "$INITIAL" \
| fzf --ansi \
--disabled --query "$INITIAL" \
--bind "change:reload:rg --line-number --no-heading --color=always --smart-case {q} || true" \
--delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}' \
--preview-window 'up,60%,border-bottom,+{2}+3/3'
Notes on placeholders:
{} the whole selected line
{q} the current query string typed in fzf
{1} first field after splitting on --delimiter (here: the file path)
{2} second field (here: the line number)
Preview window
The --preview option runs a command for the highlighted item and shows its output:
# Preview file contents with bat (falls back to cat if bat is absent)
rg --files | fzf --preview 'bat --color=always {} || cat {}'
# Preview window placement and size
fzf --preview 'cat {}' --preview-window 'right,60%'
fzf --preview 'cat {}' --preview-window 'up,50%,border-horizontal'
fzf --preview 'cat {}' --preview-window 'hidden' # toggle with CTRL-/
Jump the preview to a specific line (e.g. for grep results, {2} is the line number):
fzf --delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}' \
--preview-window '+{2}-/2' # center the matched line in the preview
fzf in shell functions
Wrap common picks in functions you can call by name.
Fuzzy-find a file and open it in your editor:
fe() {
local file
file=$(rg --files --hidden --glob '!.git/*' \
| fzf --query "$1" --select-1 --exit-0 \
--preview 'bat --color=always {} || cat {}')
[ -n "$file" ] && "${EDITOR:-vim}" "$file"
}
Fuzzy-search code, open the result at the matched line:
rgo() {
local result file line
result=$(rg --line-number --no-heading --color=always --smart-case "$1" \
| fzf --ansi --delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}') || return
file=$(printf '%s' "$result" | cut -d: -f1)
line=$(printf '%s' "$result" | cut -d: -f2)
[ -n "$file" ] && "${EDITOR:-vim}" +"$line" "$file"
}
Fuzzy-cd into any subdirectory (uses fd for speed; swap for find if needed):
fcd() {
local dir
dir=$(fd --type d --hidden --exclude .git | fzf --preview 'ls -la {}') \
&& cd "$dir" || return
}
Fuzzy-checkout a git branch:
fco() {
local branch
branch=$(git branch --all --color=never \
| sed 's/^[* ]*//; s#remotes/[^/]*/##' \
| sort -u \
| fzf --query "$1") \
&& git checkout "$branch"
}
Common workflows
Open a file by content match, jump straight to the line:
rg --line-number --no-heading --smart-case 'TODO' \
| fzf --delimiter : --preview 'bat --color=always {1} --highlight-line {2}' \
| awk -F: '{print "+"$2, $1}' \
| xargs -r vim
Kill a process interactively:
ps -ef | sed 1d | fzf -m | awk '{print $2}' | xargs -r kill -9
Stage selected files in git, with a diff preview:
git -c color.status=always status --short \
| fzf -m --ansi --preview 'git diff --color=always {2}' \
| awk '{print $2}' \
| xargs -r git add
Search history and run the chosen command immediately:
# Outside Ctrl-R, you can do this manually:
eval "$(history | fzf --tac --no-sort | sed 's/^[ ]*[0-9]*[ ]*//')"
Gotchas / tips
--ansiis required when your input contains color codes (e.g.rg --color=always), otherwise fzf shows raw escape sequences.- Use
--disabledfor reload-driven search so fzf does not fuzzy-filter the lines a second time —rgalready did the matching via{q}. - Quote
{}placeholders in previews and binds so paths with spaces survive:--preview 'bat {}'expands{}already shell-quoted by fzf. --select-1 --exit-0auto-accepts when there is exactly one match and exits cleanly when there are none — handy inside functions.FZF_DEFAULT_COMMANDonly affects fzf run with no piped stdin; explicit pipes (rg --files | fzf) ignore it.Ctrl-/toggles the preview window at runtime; pair it with--preview-window hiddento keep previews off by default.- Performance: prefer
rg --files/fdoverfindfor large trees — they respect.gitignoreand are dramatically faster.
fzf — Fuzzy Finder with ripgrep
fzf is a general-purpose command-line fuzzy finder. It reads a list from stdin,
lets you filter it interactively as you type, and prints the selection to stdout.
Paired with ripgrep (rg) it becomes a fast, ergonomic way to find files,
search file contents, jump through history, and build interactive shell tooling.
When to use it
Reach for fzf whenever you need to pick one item out of many: a file to open, a git branch to check out, a command from your shell history, or a line of code matching a search. Instead of typing exact names, you fuzzy-type a few characters and let fzf narrow the list in real time.
What is covered
The full cheatsheet snippet covers the essentials: core usage and search syntax,
the default key bindings (Ctrl-R, Ctrl-T, Alt-C), piping rg output into
fzf for live content search, configuring the preview window, and wrapping fzf in
reusable shell functions. See the markdown snippet for the copy-pasteable
reference.