Files
2025-11-29 18:17:44 +08:00

9.6 KiB

fzf Integrations Reference

Recipes for integrating fzf with other tools.

Table of Contents

ripgrep Integration

Basic Search with Preview

# Search and preview with syntax highlighting
rg --line-number --no-heading --color=always '' |
  fzf --ansi \
      --delimiter : \
      --preview 'bat --color=always {1} --highlight-line {2}' \
      --preview-window 'up,60%,border-bottom,+{2}+3/3,~3'

Open Result in Editor

# Search and open in vim at matching line
rg --line-number --no-heading --color=always '' |
  fzf --ansi --delimiter : \
      --preview 'bat --color=always {1} --highlight-line {2}' \
      --bind 'enter:become(vim {1} +{2})'

Interactive ripgrep (fzf as Frontend)

#!/usr/bin/env bash
# Save as 'rfv' and make executable

RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case"
INITIAL_QUERY="${*:-}"

fzf --ansi --disabled --query "$INITIAL_QUERY" \
    --bind "start:reload:$RG_PREFIX {q}" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --bind 'enter:become(vim {1} +{2})'

Toggle Between ripgrep and fzf Mode

#!/usr/bin/env bash
# CTRL-R for ripgrep mode, CTRL-F for fzf mode

rm -f /tmp/rg-fzf-{r,f}
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case"

fzf --ansi --disabled --query "${*:-}" \
    --bind "start:reload($RG_PREFIX {q})+unbind(ctrl-r)" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --bind "ctrl-f:unbind(change,ctrl-f)+change-prompt(fzf> )+enable-search+rebind(ctrl-r)+transform-query(echo {q} > /tmp/rg-fzf-r; cat /tmp/rg-fzf-f)" \
    --bind "ctrl-r:unbind(ctrl-r)+change-prompt(rg> )+disable-search+reload($RG_PREFIX {q} || true)+rebind(change,ctrl-f)+transform-query(echo {q} > /tmp/rg-fzf-f; cat /tmp/rg-fzf-r)" \
    --prompt 'rg> ' \
    --delimiter : \
    --header 'CTRL-R (ripgrep) / CTRL-F (fzf)' \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --bind 'enter:become(vim {1} +{2})'

fd Integration

File Search with Preview

# Find files with preview
fd --type f --hidden --follow --exclude .git |
  fzf --preview 'bat --color=always {}' \
      --bind 'enter:become(vim {})'

Set as Default Command

# In ~/.bashrc or ~/.zshrc
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'

Toggle Files/Directories

fd --type f |
  fzf --prompt 'Files> ' \
      --header 'CTRL-D: Dirs / CTRL-F: Files' \
      --bind 'ctrl-d:change-prompt(Dirs> )+reload(fd --type d)' \
      --bind 'ctrl-f:change-prompt(Files> )+reload(fd --type f)'

Filter by Extension

# Find Python files
fd --extension py | fzf --preview 'bat --color=always {}'

# Multiple extensions
fd --extension js --extension ts | fzf

bat Integration

Preview with Syntax Highlighting

fzf --preview 'bat --color=always --style=numbers --line-range :500 {}'

Highlight Specific Line

# With line number from grep/rg
fzf --preview 'bat --color=always {1} --highlight-line {2}' --delimiter :

Preview Window Settings

# Show first 500 lines with header
fzf --preview 'bat --color=always --style=header,numbers --line-range :500 {}'

# Plain output for speed
fzf --preview 'bat --color=always --style=plain {}'

Git Integration

Select Branches

# Checkout branch
git branch --all | fzf | xargs git checkout

# With preview of recent commits
git branch --all |
  fzf --preview 'git log --oneline -20 {}' |
  sed 's/^[* ]*//' | sed 's/remotes\/origin\///' |
  xargs git checkout

Select Commits

# Copy commit hash
git log --oneline |
  fzf --preview 'git show --color=always {1}' |
  cut -d' ' -f1

# Interactive rebase from selected commit
git log --oneline |
  fzf --preview 'git show --color=always {1}' |
  cut -d' ' -f1 |
  xargs -I {} git rebase -i {}^

Stage Files Interactively

# Select unstaged files to add
git status --short |
  fzf --multi --preview 'git diff --color=always {2}' |
  awk '{print $2}' |
  xargs git add

View Changed Files

# Open changed files
git status --short |
  fzf --multi --preview 'git diff --color=always {2}' |
  awk '{print $2}' |
  xargs -o vim

Stash Management

# Select and apply stash
git stash list |
  fzf --preview 'git stash show -p {1}' --delimiter : |
  cut -d: -f1 |
  xargs git stash apply

fzf-git.sh Key Bindings

Highly recommended: fzf-git.sh

Provides keybindings:

  • CTRL-G CTRL-F - Files from git status
  • CTRL-G CTRL-B - Branches
  • CTRL-G CTRL-T - Tags
  • CTRL-G CTRL-R - Remotes
  • CTRL-G CTRL-H - Commit hashes
  • CTRL-G CTRL-S - Stashes
  • CTRL-G CTRL-L - Reflogs
  • CTRL-G CTRL-W - Worktrees
  • CTRL-G CTRL-E - Each ref (git for-each-ref)

Docker Integration

Select Containers

# Attach to running container
docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}' |
  fzf --delimiter '\t' --with-nth 1,2,3 |
  cut -f1 |
  xargs -o docker attach

Select Images

# Remove images
docker images --format '{{.Repository}}:{{.Tag}}\t{{.ID}}\t{{.Size}}' |
  fzf --multi --delimiter '\t' |
  cut -f2 |
  xargs docker rmi

View Logs

# Follow container logs
docker ps --format '{{.Names}}' |
  fzf --preview 'docker logs --tail 50 {}' |
  xargs -o docker logs -f

Kubernetes Integration

Select Pods

# Get shell in pod
kubectl get pods --all-namespaces -o wide |
  fzf --header-lines=1 |
  awk '{print "-n", $1, $2}' |
  xargs -o kubectl exec -it -- /bin/sh

Pod Logs with Follow

pods() {
  kubectl get pods --all-namespaces |
    fzf --info=inline --layout=reverse --header-lines=1 \
        --prompt "$(kubectl config current-context)> " \
        --header $'CTRL-O: logs | CTRL-R: reload\n' \
        --bind 'start,ctrl-r:reload:kubectl get pods --all-namespaces' \
        --bind 'ctrl-o:execute:kubectl logs --namespace {1} {2} | less' \
        --preview-window up:follow \
        --preview 'kubectl logs --follow --tail=100 --namespace {1} {2}'
}

Context Switching

# Switch context
kubectl config get-contexts -o name |
  fzf --preview 'kubectl config view -o jsonpath="{.contexts[?(@.name==\"{}\")]}"' |
  xargs kubectl config use-context

tmux Integration

Select Sessions

# Attach to session
tmux list-sessions -F '#S' |
  fzf --preview 'tmux capture-pane -pt {}' |
  xargs tmux switch-client -t

Select Windows

# Switch to window
tmux list-windows -a -F '#S:#W' |
  fzf --preview 'tmux capture-pane -pt {}' |
  xargs tmux switch-client -t

tmux Popup

# Use fzf in tmux popup
fzf --tmux center,80%

Shell Function Recipes

Interactive cd

# cd with preview
fcd() {
  local dir
  dir=$(fd --type d --hidden --follow --exclude .git |
    fzf --preview 'tree -C {} | head -50') &&
  cd "$dir"
}

Edit Recent Files

# Edit recent git files
vg() {
  local files
  files=$(git ls-files --modified --others --exclude-standard |
    fzf --multi --preview 'bat --color=always {}') &&
  vim $files
}

Kill Process

# Interactive process killer
fkill() {
  local pid
  pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')
  if [ -n "$pid" ]; then
    echo "$pid" | xargs kill -9
  fi
}

Search History

# Enhanced history search
fh() {
  eval $(history | fzf +s --tac | sed 's/ *[0-9]* *//')
}

Open GitHub PR

# Select and open PR
gpr() {
  gh pr list |
    fzf --preview 'gh pr view {1}' |
    awk '{print $1}' |
    xargs gh pr checkout
}

Man Page Viewer

# Browse man pages
fman() {
  man -k . |
    fzf --prompt='Man> ' --preview 'man {1}' |
    awk '{print $1}' |
    xargs man
}

Environment Variables

Add to your shell config:

# Better defaults
export FZF_DEFAULT_OPTS='
  --height 40%
  --layout reverse
  --border
  --info inline
  --preview-window right,50%,border-left
'

# CTRL-T with preview
export FZF_CTRL_T_OPTS="
  --preview 'bat --color=always --style=numbers --line-range :500 {}'
  --bind 'ctrl-/:change-preview-window(down|hidden|)'
"

# CTRL-R with preview
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'
  --header 'CTRL-Y to copy'
"

# ALT-C with tree preview
export FZF_ALT_C_OPTS="
  --preview 'tree -C {} | head -100'
"

Tips

Performance

  • Avoid --ansi in FZF_DEFAULT_OPTS (slows scanning)
  • Use --nth sparingly (requires tokenization)
  • Prefer string delimiter over regex
  • Use fd instead of find for file listing

Debugging

# Test preview command
echo "test.txt" | fzf --preview 'bat --color=always {}'

# See what fzf receives
fzf --preview 'echo {} | cat -v'

Escaping

# Escape special characters in queries
fzf --query 'foo\ bar'  # Literal space

# In shell scripts, quote properly
fzf --bind "enter:execute(vim '{}')"