Skip to content

fzf — Fuzzy Finder Primer

Why This Matters

fzf turns any list into an interactive, filterable menu. Piping command output through fzf eliminates slow manual scanning of logs, process lists, git branches, and Kubernetes resources. Once you wire it into your shell, every lookup operation becomes sub-second.

Name origin: fzf = fuzzy finder. Written in Go by Junegunn Choi, first released in 2013. It has 65,000+ GitHub stars, making it one of the most popular CLI tools ever created.

Fun fact: fzf integrates directly with bash/zsh: Ctrl+R fuzzy-searches command history (instead of the default backward search), Ctrl+T inserts a file path, and Alt+C changes to a directory. These bindings alone are worth the install.

Core Concepts

Basic Usage

fzf reads lines from stdin and presents an interactive fuzzy-match selector:

# Pick a file from the current tree
find . -type f | fzf

# Pick a running process
ps aux | fzf

# Pick a Kubernetes pod
kubectl get pods -o name | fzf

The search is fuzzy by default — typing mncfg matches main_config.yaml. Use a single quote prefix for exact matching: 'exact.

Search Syntax

Token Meaning
sbtrkt Fuzzy match
'wild Exact match
^music Prefix exact match
.mp3$ Suffix exact match
!fire Inverse match
!^music Inverse prefix

Combine tokens with spaces (AND logic): ^src .go$ !test.

Piping and Filtering

fzf shines when combined with pipes:

# Search environment variables
env | fzf

# Grep results as selectable list
grep -rn "ERROR" /var/log/ | fzf

# Select and kill a process
kill -9 $(ps aux | fzf | awk '{print $2}')

# Select a docker container to exec into
docker exec -it $(docker ps --format '{{.Names}}' | fzf) bash

Multi-Select

Pass --multi (or -m) to select multiple items with Tab:

# Select multiple files to delete
rm $(find /tmp -type f -mtime +30 | fzf -m)

# Select multiple pods to describe
kubectl get pods -o name | fzf -m | xargs -I{} kubectl describe {}

Preview Window

The --preview flag shows a live preview of the selected item:

# File preview with syntax highlighting (requires bat)
fzf --preview 'bat --color=always {}'

# Pod preview
kubectl get pods -o name | fzf --preview 'kubectl describe {}'

# Git log preview
git log --oneline | fzf --preview 'git show {1}'

Control preview layout:

fzf --preview 'cat {}' --preview-window=right:60%:wrap
fzf --preview 'cat {}' --preview-window=up:40%:hidden --bind '?:toggle-preview'

Shell Integration

Remember: The three fzf shell bindings: "R-T-C" — Ctrl-R (history search), Ctrl-T (file path insert), Alt-C (cd to directory). These three bindings replace the default shell behaviors with fuzzy-matching equivalents.

History Search (Ctrl-R)

fzf replaces the default reverse history search:

# Add to .bashrc or .zshrc
eval "$(fzf --bash)"    # bash
eval "$(fzf --zsh)"     # zsh

Now Ctrl-R opens a fuzzy-searchable command history.

Directory Navigation (Alt-C)

Alt-C fuzzy-searches directories and cds into the selection.

File Completion (Ctrl-T)

Ctrl-T inserts a fuzzy-selected file path at the cursor.

Keybindings

Custom bindings inside the fzf interface:

fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)' \
    --bind 'ctrl-e:execute(vim {})' \
    --bind 'ctrl-d:half-page-down' \
    --bind 'ctrl-u:half-page-up'

Git Integration

# Checkout a branch interactively
git branch -a | fzf | xargs git checkout

# Interactive git log browser
git log --oneline --all | fzf --preview 'git show --stat {1}'

# Stage files interactively
git diff --name-only | fzf -m | xargs git add

Kubectl Integration

# Switch namespace
kubectl get ns -o name | sed 's|namespace/||' | fzf | xargs kubectl config set-context --current --namespace

# Tail logs from a selected pod
kubectl get pods -o name | fzf | xargs -I{} kubectl logs -f {}

# Exec into a selected container
kubectl get pods -o name | fzf --preview 'kubectl get {} -o yaml' | xargs -I{} kubectl exec -it {} -- sh

Environment Variables

Variable Purpose
FZF_DEFAULT_COMMAND Command used to generate input (e.g., fd --type f)
FZF_DEFAULT_OPTS Default options applied to every invocation
FZF_CTRL_T_COMMAND Command for Ctrl-T file finder
FZF_ALT_C_COMMAND Command for Alt-C directory finder

Under the hood: FZF_DEFAULT_COMMAND only applies when fzf is invoked without stdin piped to it. When you run find . | fzf, the FZF_DEFAULT_COMMAND is ignored because stdin is already connected. This is by design — the environment variable provides a default source when fzf has nothing piped in.

export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'

Practical Patterns

Combining with Other Tools

# fd + fzf + bat
fd --type f | fzf --preview 'bat --color=always {}'

# rg + fzf (search then select)
rg --line-number "TODO" | fzf --delimiter=: --preview 'bat --color=always {1} --highlight-line {2}'

Tips

  • Use fd or rg --files as FZF_DEFAULT_COMMAND instead of find — 5-10x faster
  • Set --height 40% to avoid full-screen takeover in quick lookups
  • Use --ansi when piping colored output

Gotcha: fzf reads the entire input before displaying results. If you pipe a very slow command (e.g., find / on a massive filesystem), fzf will appear to hang. Use fd instead of find for filesystem searches — it respects .gitignore, skips hidden directories by default, and is 5-10x faster.

One-liner: The single most useful fzf alias for DevOps: alias kp='kubectl get pods -A -o name | fzf --preview "kubectl describe {}" | xargs kubectl delete' — interactive pod killer with preview. Adjust the final command to taste.


Wiki Navigation