Skip to content
C.W.K.
Stream
Lesson 02 of 08 · published

fzf: Fuzzy Finder Magic

~13 min · fzf, fuzzy, history

Level 0Window Tourist
0 XP0/95 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

The interactive line picker

fzf reads stdin, shows an interactive prompt where you type to filter, and prints the selected line(s) on stdout. Once integrated into your shell, it transforms history search, file pickers, and process selection into a single muscle-memory.

Install + integrate

brew install fzf
$(brew --prefix)/opt/fzf/install   # adds keybindings + completion

Shell integration and its key bindings vary by fzf version and installation method. Follow the documentation installed with the version you use, then verify which component owns each key:

  • Ctrl+R — fuzzy history search. Replaces the default. Type any substring; matches show; Enter to run.
  • Ctrl+T — fuzzy file picker. Type at a prompt, hit Ctrl+T, pick a file, the path inserts into the command line.
  • Alt+C — fuzzy cd into a subdirectory.

Standalone use

# Inspect a selected process before choosing any signal
pid=$(ps -axo pid=,command= | fzf | awk '{print $1}')
ps -p "$pid" -o pid=,ppid=,user=,command=

# Switch a selected local branch with modern Git
branch=$(git for-each-ref --format='%(refname:short)' refs/heads | fzf)
[[ -n "$branch" ]] && git switch "$branch"

# Open a selected regular file
file=$(fd --type f | fzf)
[[ -n "$file" ]] && "$EDITOR" -- "$file"

Customizing

# Better defaults
export FZF_DEFAULT_OPTS="--height 40% --reverse --border --info=inline"
# Use fd for the file pickers (faster than find)
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"

Why everyone falls in love with it

You stop typing complete filenames, complete branch names, complete PIDs. Fuzzy substring + arrow-key + Enter is faster than tab completion for almost every interactive selection task.

Selection is not authorization

fzf returns text. Validate empty selections, parse stable machine-readable fields, and show the resolved target before a destructive action. Start process control with inspection and a graceful owner-aware stop, never an automatic SIGKILL.

One line is not always one value

Filenames can contain newlines, so line-oriented pipelines are not a universal path protocol. Prefer NUL-delimited producers and consumers when arbitrary filesystem names must survive selection, or constrain and validate the input set explicitly.

Separate the selector from the executor

Let fd and fzf narrow candidates, then validate the resolved value in a separate step before changing state. This keeps cancellation, parsing, and authorization visible.

Code

Set up fzf shell integration·bash
brew install fzf
$(brew --prefix)/opt/fzf/install
# After restart, try Ctrl+R, Ctrl+T, Alt+C

External links

Exercise

Install fzf and follow the current integration instructions for your shell. Try Ctrl+R for history and Ctrl+T for file selection. For branches, print the local branch list, capture one fzf selection in a variable, and inspect the empty-selection and current-branch cases. Print the exact git switch command first; run it only after confirming the resolved branch.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.