Skip to content

fzf - Fuzzy Finder

fzf is a general-purpose command-line fuzzy finder that enables interactive filtering of any list: files, command history, processes, git branches, and more.

Installation

macOS (Homebrew)

brew install fzf

# Install shell keybindings and completion
$(brew --prefix)/opt/fzf/install

Linux (Debian/Ubuntu)

sudo apt install fzf

# Or install from git for latest features
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

Basic Usage

fzf                               # Find files interactively
fzf -m                            # Multi-select mode (Tab to select)
cmd | fzf                         # Filter any list
fzf < list.txt                    # Filter from file

Search Syntax

Pattern Match Type
abc Fuzzy match
'abc Exact match (quoted)
^abc Prefix match
abc$ Suffix match
!abc Inverse match
!^abc Inverse prefix
abc | def OR operator

Combine patterns with spaces (AND):

# Files containing "test" AND "spec"
fzf --query "test spec"

# Python files with "config" but not "test"
fzf --query ".py config !test"

Environment Variables

FZF_DEFAULT_COMMAND

Defines the default command used when fzf is launched without input:

# Use fd for better performance and .gitignore support
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'

# With ripgrep (file list mode)
export FZF_DEFAULT_COMMAND='rg --files --hidden --follow --glob "!.git/*"'

# Traditional find (slower, no .gitignore support)
export FZF_DEFAULT_COMMAND='find . -type f'

FZF_DEFAULT_OPTS

Default options applied to all fzf invocations:

export FZF_DEFAULT_OPTS='
  --height=40%
  --layout=reverse
  --border=rounded
  --info=inline
  --margin=1
  --padding=1
'

Shell Integration Variables

# Ctrl+T: File search
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_CTRL_T_OPTS="
  --preview 'bat --color=always --line-range :500 {}'
  --bind 'ctrl-/:toggle-preview'
"

# Ctrl+R: History search
export FZF_CTRL_R_OPTS="
  --preview 'echo {}'
  --preview-window up:3:hidden:wrap
  --bind 'ctrl-/:toggle-preview'
  --bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'
"

# Alt+C: Directory navigation
export FZF_ALT_C_COMMAND='fd --type d --hidden --follow --exclude .git'
export FZF_ALT_C_OPTS="
  --preview 'eza --tree --color=always {} | head -200'
"

Shell Keybindings

After running the install script, these keybindings are available:

Key Action
Ctrl+T Paste selected file path(s)
Ctrl+R Search command history
Alt+C cd to selected directory

Keybindings Inside fzf

Key Action
Enter Accept selection
Tab Mark item (multi-select)
Shift+Tab Unmark item
Ctrl+A Select all
Ctrl+D Deselect all
Ctrl+J / Ctrl+K Move down/up
Ctrl+/ Toggle preview
Ctrl+C / Esc Cancel

Preview Options

Add a preview window to see file contents:

# Basic preview with bat
fzf --preview 'bat --color=always {}'

# Preview with line numbers
fzf --preview 'bat --color=always --style=numbers {}'

# Preview window positioning
fzf --preview-window=right:60%        # Right side, 60% width
fzf --preview-window=up:40%           # Top, 40% height
fzf --preview-window=down:50%:wrap    # Bottom with word wrap
fzf --preview-window=hidden           # Hidden by default

# Toggle preview with Ctrl+/
fzf --preview 'bat --color=always {}' --bind 'ctrl-/:toggle-preview'

Preview for Different Content Types

# Files with syntax highlighting
fzf --preview 'bat --color=always --line-range :500 {}'

# Directories with tree view
fzf --preview 'eza --tree --color=always --level=2 {}'

# Git commits
git log --oneline | fzf --preview 'git show --color=always {1}'

# Processes
ps aux | fzf --preview 'echo {}' --preview-window=wrap

Integration with fd

fd and fzf work together for faster file finding:

# Use fd as the default command
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'

# Find and open files
fd --type f | fzf --preview 'bat --color=always {}' | xargs -r nvim

# Find specific extensions
fd -e py | fzf --preview 'bat --color=always {}'

# Find directories
fd --type d | fzf --preview 'eza --tree {}'

Tab Completion

fzf provides fuzzy completion for common commands:

# Enable in bash
source /opt/homebrew/opt/fzf/shell/completion.bash

# Enable in zsh
source /opt/homebrew/opt/fzf/shell/completion.zsh

Trigger Completion

Use ** as the trigger sequence:

# File/directory completion
vim **<TAB>
cd ~/projects/**<TAB>

# Process completion
kill **<TAB>

# Host completion
ssh **<TAB>

# Environment variable
export **<TAB>

Custom Completion

# Custom completion for git checkout
_fzf_complete_git() {
    if [[ "$@" == "git checkout"* ]]; then
        _fzf_complete --preview 'git log --oneline -20 {}' -- "$@" < <(
            git branch --all | sed 's/^..//' | sed 's#remotes/origin/##'
        )
    else
        eval "zle ${fzf_default_completion:-expand-or-complete}"
    fi
}

Advanced Usage

Multi-Select Operations

# Select multiple files to edit
nvim $(fzf -m)

# Select multiple files to delete
fzf -m | xargs rm

# Select multiple files with preview
fzf -m --preview 'bat --color=always {}' --bind 'ctrl-a:select-all'

Header and Prompt

fzf --header 'Select a file to edit'
fzf --prompt 'Files> '
fzf --pointer '>'
fzf --marker '+'

Custom Bindings

# Execute command on selection
fzf --bind 'enter:execute(nvim {})'

# Execute and close
fzf --bind 'ctrl-o:execute-silent(code {})+abort'

# Reload results
fzf --bind 'ctrl-r:reload(fd --type f)'

# Chain multiple actions
fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)+abort'

Theming

Built-in Color Schemes

# Dark theme
export FZF_DEFAULT_OPTS='--color=dark'

# Light theme
export FZF_DEFAULT_OPTS='--color=light'

# 16 color mode
export FZF_DEFAULT_OPTS='--color=16'

Custom Colors

export FZF_DEFAULT_OPTS='
  --color=fg:#c0caf5,bg:#1a1b26,hl:#bb9af7
  --color=fg+:#c0caf5,bg+:#292e42,hl+:#7dcfff
  --color=info:#7aa2f7,prompt:#7dcfff,pointer:#7dcfff
  --color=marker:#9ece6a,spinner:#9ece6a,header:#9ece6a
'
export FZF_DEFAULT_OPTS='
  --color=fg:#c0caf5,bg:#24283b,hl:#ff9e64
  --color=fg+:#c0caf5,bg+:#292e42,hl+:#ff9e64
  --color=info:#7aa2f7,prompt:#7dcfff,pointer:#7dcfff
  --color=marker:#9ece6a,spinner:#9ece6a,header:#9ece6a
  --color=border:#27a1b9
'
export FZF_DEFAULT_OPTS='
  --color=bg+:#313244,bg:#1e1e2e,spinner:#f5e0dc,hl:#f38ba8
  --color=fg:#cdd6f4,header:#f38ba8,info:#cba6f7,pointer:#f5e0dc
  --color=marker:#f5e0dc,fg+:#cdd6f4,prompt:#cba6f7,hl+:#f38ba8
'

Custom Functions

Find and Edit

# Find file and open in editor
fe() {
    local file
    file=$(fd --type f --hidden --follow --exclude .git | \
           fzf --preview 'bat --color=always --line-range :500 {}')
    [[ -n "$file" ]] && ${EDITOR:-nvim} "$file"
}

Find and cd

# Find directory and cd
fcd() {
    local dir
    dir=$(fd --type d --hidden --follow --exclude .git | \
          fzf --preview 'eza --tree --color=always {} | head -200')
    [[ -n "$dir" ]] && cd "$dir"
}

Git Integration

# Checkout branch
fco() {
    local branch
    branch=$(git branch --all | grep -v HEAD | sed 's/.* //' | \
             sed 's#remotes/origin/##' | sort -u | \
             fzf --preview 'git log --oneline -20 {}')
    [[ -n "$branch" ]] && git checkout "$branch"
}

# Browse git log
fgl() {
    git log --oneline --color=always | \
    fzf --ansi --preview 'git show --color=always {1}' \
        --bind 'enter:execute(git show {1} | less -R)'
}

# Add files interactively
fga() {
    local files
    files=$(git status --short | \
            fzf -m --preview 'git diff --color=always {2}' | \
            awk '{print $2}')
    [[ -n "$files" ]] && echo "$files" | xargs git add
}

Process Management

# Kill process
fkill() {
    local pid
    pid=$(ps aux | sed 1d | fzf -m --preview 'echo {}' | awk '{print $2}')
    [[ -n "$pid" ]] && echo "$pid" | xargs kill -${1:-9}
}

Docker Integration

# Docker container shell
fdsh() {
    local container
    container=$(docker ps --format '{{.Names}}' | fzf --preview 'docker inspect {}')
    [[ -n "$container" ]] && docker exec -it "$container" sh
}

# Docker logs
fdl() {
    local container
    container=$(docker ps -a --format '{{.Names}}' | fzf)
    [[ -n "$container" ]] && docker logs -f "$container"
}

SSH Host Selection

# SSH to host from config
fssh() {
    local host
    host=$(grep "^Host " ~/.ssh/config | awk '{print $2}' | \
           fzf --preview 'grep -A5 "^Host {}" ~/.ssh/config')
    [[ -n "$host" ]] && ssh "$host"
}

Add to ~/.bashrc or ~/.zshrc:

# Use fd for file finding (faster, respects .gitignore)
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'

# Default options
export FZF_DEFAULT_OPTS='
  --height=60%
  --layout=reverse
  --border=rounded
  --info=inline
  --preview-window=right:50%:hidden
  --bind ctrl-/:toggle-preview
  --bind ctrl-a:select-all
  --bind ctrl-d:deselect-all
'

# Ctrl+T: File preview with bat
export FZF_CTRL_T_OPTS="
  --preview 'bat --color=always --style=numbers --line-range :500 {}'
"

# Alt+C: Directory preview with eza
export FZF_ALT_C_OPTS="
  --preview 'eza --tree --color=always --level=2 {} | head -200'
"

# Ctrl+R: History search
export FZF_CTRL_R_OPTS="
  --preview 'echo {}'
  --preview-window down:3:hidden:wrap
  --bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'
"

Performance Tips

  1. Use fd instead of find: Much faster, respects .gitignore
  2. Limit preview scope: Use --line-range with bat to limit file preview
  3. Hide preview by default: Use --preview-window=hidden for large directories
  4. Exclude unnecessary directories: Use --exclude to skip node_modules, .git, etc.

Troubleshooting

Keybindings Not Working

Ensure shell integration is sourced:

# Bash
source /opt/homebrew/opt/fzf/shell/key-bindings.bash

# Zsh
source /opt/homebrew/opt/fzf/shell/key-bindings.zsh

Alt+C Not Working on macOS

Option/Alt key might be mapped differently. In iTerm2:

  1. Preferences > Profiles > Keys
  2. Set "Left Option Key" to "Esc+"

Preview Not Showing

Check if bat is installed:

brew install bat

For directory previews, check if eza is installed:

brew install eza
  • fd - Fast file finder
  • ripgrep - Fast grep
  • bat - Syntax highlighting
  • eza - Modern ls