This collection contains small shell functions to integrate fzf into everyday workflows:
file navigation, searching with ripgrep, Git, processes, man pages, and more.
Usage: Put the functions into a file like ~/.fzf_scripts.sh and source it from your
.bashrc / .zshrc. Adjust tools like bat, rg, fd, wl-copy as needed.
Sets reasonable default options for fzf and tweaks the standard CTRL‑T / ALT‑C / CTRL‑R
bindings for better previews and navigation.
export FZF_DEFAULT_OPTS="
--height 80%
--layout=reverse
--border
--ansi
--bind 'tab:toggle-down,btab:toggle-up'
"
export FZF_CTRL_T_OPTS="
--walker-skip .git,node_modules,target
--preview 'bat -n --color=always {}'
--bind 'ctrl-/:change-preview-window(down|hidden|)'
"
export FZF_ALT_C_OPTS="
--preview 'ls --color=always {}'
"
export FZF_CTRL_R_OPTS="
--sort
--preview 'echo {}'
"
ff / ffm let you pick files with fzf and open them in your $EDITOR.
cdd lets you jump to any directory found by fd.
cdbh navigates through your shell directory stack via fzf.
ff () {
local file
file=$( fd . --type f 2> /dev/null | fzf --prompt=' file> ' ) || return
" ${EDITOR:- vim} " " $file "
}
ffm () {
local files
files=$( fd . --type f 2> /dev/null | fzf --multi --prompt=' files> ' ) || return
" ${EDITOR:- vim} " $files
}
cdd () {
local dir
dir=$( fd . --type d 2> /dev/null | fzf --prompt=' cd> ' ) || return
cd " $dir " || return
}
cdbh () {
local dir
dir=$( dirs -v | fzf --tac --prompt=' dirs> ' | awk ' {print $2}' ) || return
cd " $dir " || return
}
frg combines ripgrep and fzf: search for a pattern, select a match, and open the file at
the correct line in your editor.
frgf finds all files containing a pattern and lets you select one or many to edit.
frg () {
local query file
query=" $1 "
if [[ -z " $query " ]]; then
echo " Usage: frg <pattern>"
return 1
fi
file=$(
rg --color=always --line-number --no-heading " $query " \
| fzf --ansi \
--prompt=" rg: $query > " \
--delimiter ' :' \
--preview ' bat --color=always --style=numbers --line-range {2}: {1}' \
--preview-window=' up,60%,border-bottom'
) || return
local filename lineno
filename=$( echo " $file " | cut -d: -f1)
lineno=$( echo " $file " | cut -d: -f2)
" ${EDITOR:- vim} " " +${lineno} " " $filename "
}
frgf () {
local query files
query=" $1 "
if [[ -z " $query " ]]; then
echo " Usage: frgf <pattern>"
return 1
fi
files=$(
rg " $query " --files-with-matches 2> /dev/null \
| fzf --multi --prompt=" rg files: $query > "
) || return
" ${EDITOR:- vim} " $files
}
fglog shows an interactive Git log with a preview of the selected commit.
fgcopy lets you pick a commit and copies its hash to your clipboard (or prints it).
fgb lists all branches (local and remote) and checks out the selected one.
fglog () {
git log --graph --color=always \
--format=' %C(auto)%h%d %s %C(black)%C(bold)%cr%Creset %C(green)%an' \
| fzf --ansi --no-sort --reverse --tiebreak=index \
--preview ' echo {} | grep -oE "^[^ ]+" | xargs git show --color=always' \
--preview-window=right:60%
}
fgcopy () {
local hash
hash=$(
git log --color=always --oneline \
| fzf --ansi --no-sort --reverse \
| awk ' {print $1}'
) || return
if command -v wl-copy > /dev/null 2>&1 ; then
printf ' %s' " $hash " | wl-copy
elif command -v xclip > /dev/null 2>&1 ; then
printf ' %s' " $hash " | xclip -selection clipboard
elif command -v xsel > /dev/null 2>&1 ; then
printf ' %s' " $hash " | xsel --clipboard --input
else
echo " $hash "
fi
}
fgb () {
local branch
branch=$(
git branch --all --color=always | sed ' s/^..//' \
| fzf --ansi --prompt=' branch> ' \
--preview ' git log --oneline --graph --decorate --color=always --max-count=20 {1}' \
--preview-window=right:60%
) || return
git checkout " $( echo " $branch " | sed ' s#remotes/[^/]*/##' ) "
}
fkill lists running processes and lets you kill one or many via fzf.
fenv searches your environment variables and prints the selected value.
fman offers a fuzzy interface over man -k and previews man pages.
fkill () {
local pid
pid=$(
ps -ef | sed 1d \
| fzf --multi --prompt=' kill> ' \
--preview ' echo {}' \
| awk ' {print $2}'
) || return
if [[ -n " $pid " ]]; then
echo " $pid " | xargs kill -" ${1:- 9} "
fi
}
fenv () {
local out
out=$( env | fzf --prompt=' env> ' ) || return
echo " ${out#* =} "
}
fman () {
local MAN_CMD
MAN_CMD=" ${MAN:-/ usr/ bin/ man} "
if [[ -n " $1 " ]]; then
" $MAN_CMD " " $@ "
return
fi
local selection page
selection=$(
" $MAN_CMD " -k . 2> /dev/null \
| fzf --prompt=' man> ' --reverse \
--preview=" echo {} | awk '{print \$ 1\" \"\$ 2}' | sed 's/(/ /;s/)//' | xargs $MAN_CMD " \
--preview-window=right:60%
) || return
page=$( echo " $selection " | awk ' {print $1"."$2}' | tr -d ' ()' )
" $MAN_CMD " " $page "
}
fh provides an interactive fuzzy search over your shell history and executes the chosen
command.
fgchanged shows changed Git files and opens the selected ones in your editor.
frm lets you pick files in the current directory to remove, instead of passing names
manually to rm.
fh () {
local cmd
if [[ -n " $ZSH_NAME " ]]; then
cmd=$(
fc -l 1 \
| fzf --tac --no-sort --prompt=' history> ' --preview ' echo {}' \
| sed ' s/^[[:space:]]*[0-9]\+[[:space:]]*//'
) || return
else
cmd=$(
history \
| fzf --tac --no-sort --prompt=' history> ' --preview ' echo {}' \
| sed ' s/^[[:space:]]*[0-9]\+[[:space:]]*//'
) || return
fi
print -s " $cmd " 2> /dev/null || history -s " $cmd " 2> /dev/null
eval " $cmd "
}
fgchanged () {
local files
files=$(
git diff --name-only --diff-filter=ACMRTUXB HEAD 2> /dev/null \
| fzf --multi --prompt=' changed> ' \
--preview ' git diff --color=always -- {-1} | sed -n "1,200p"' \
--preview-window=right:60%
) || return
" ${EDITOR:- vim} " $files
}
frm () {
if [[ " $# " -ne 0 ]]; then
command rm " $@ "
return
fi
local files
files=$(
find . -maxdepth 1 -type f 2> /dev/null \
| sed ' s#^\./##' \
| fzf --multi --prompt=' rm> ' \
--preview ' ls -l --color=always {}'
) || return
echo " $files " | xargs -r rm
}
Recent files (using fd + stat)
Lists files in the current tree, sorted by modification time, and lets you pick one or more to edit.
frecent () {
local files
files=$(
fd . --type f 2> /dev/null \
| xargs -r stat -c ' %Y %n' 2> /dev/null \
| sort -nr \
| cut -d' ' -f2- \
| fzf --multi --prompt=' recent> '
) || return
" ${EDITOR:- vim} " $files
}
SSH host picker (from ~/.ssh/config and known_hosts)
Collects hostnames from SSH config and known_hosts, lets you pick one, then connects.
fssh () {
local host
host=$(
{
grep -iE ' ^Host ' ~ /.ssh/config 2> /dev/null | awk ' {for (i=2;i<=NF;i++) print $i}'
[ -f ~ /.ssh/known_hosts ] && cut -f1 -d' ' ~ /.ssh/known_hosts | sed ' s/[,\ ]/\n/g'
} | sed ' s/[*,]//g' | sed ' /^$/d' | sort -u \
| fzf --prompt=' ssh> '
) || return
ssh " $host "
}
Kubernetes context and namespace picker (kubectl)
Quickly switch kubectl context and namespace via fzf.
fkc () {
local ctx
ctx=$(
kubectl config get-contexts -o name 2> /dev/null \
| fzf --prompt=' kubectx> '
) || return
kubectl config use-context " $ctx "
}
fkns () {
local ns
ns=$(
kubectl get ns -o jsonpath=' {range .items[*]}{.metadata.name}{"\n"}{end}' 2> /dev/null \
| fzf --prompt=' kube-ns> '
) || return
kubectl config set-context --current --namespace=" $ns "
}
Docker container and image picker
Start a shell in a running container or run a new container from an image, both via fzf.
fdocker_shell () {
local id
id=$(
docker ps --format ' {{.ID}} {{.Image}} {{.Names}}' \
| fzf --prompt=' docker ps> ' --preview ' echo {}'
) || return
id=${id%% * }
docker exec -it " $id " " ${1:-/ bin/ bash} "
}
fdocker_run () {
local img
img=$(
docker images --format ' {{.Repository}}:{{.Tag}} {{.ID}}' \
| fzf --prompt=' docker image> '
) || return
img=${img%% * }
docker run -it --rm " $img " " ${@:-/ bin/ bash} "
}
List stashes with description, preview their diff, and apply or pop the selected one.
fgstash () {
local line stash
line=$(
git stash list \
| fzf --prompt=' stash> ' \
--preview ' echo {} | cut -d: -f1 | xargs git stash show -p --color=always' \
--preview-window=right:60%
) || return
stash=$( echo " $line " | cut -d: -f1)
case " ${1:- apply} " in
apply) git stash apply " $stash " ;;
pop) git stash pop " $stash " ;;
show) git stash show -p " $stash " ;;
* ) echo " Usage: fgstash [apply|pop|show]" ;;
esac
}
Open URL from browser history (example: sqlite + fzf)
Example for Chromium‑based browsers; adjust DB path and query for your setup.
furl_chromium () {
local db url
db=" $HOME /.config/chromium/Default/History"
[ -f " $db " ] || { echo " Chromium history DB not found" ; return 1; }
url=$(
sqlite3 " $db " \
" select url, title, last_visit_time from urls order by last_visit_time desc" 2> /dev/null \
| awk -F' |' ' {printf "%s\t%s\n", $1, $2}' \
| fzf --prompt=' url> ' --with-nth=2 --preview ' echo {1}' \
| cut -f1
) || return
[ -n " $url " ] && xdg-open " $url " > /dev/null 2>&1 &
}
Test file runner (Go/Python/Jest)
Finds test files and runs the selected one(s) with appropriate runner.
ftest () {
local test runner
test=$(
fd --extension _test.go,test.py,spec.js,test.js . \
| fzf --multi --prompt=' test> '
) || return
case " $test " in
* .go) runner=' go test' ;;
* .py) runner=' pytest' ;;
* .js) runner=' npm test --' ;;
* ) return 1 ;;
esac
echo " $test " | xargs -d ' \n' $runner
}
Cargo crate dependency picker
Fuzzy search through Cargo.toml dependencies and opens docs or repo.
fcargo_dep () {
local dep
dep=$(
grep -E ' ^[^#]*=' Cargo.toml \
| sed ' s/.*= "\([^"]*\)".*/\1/' \
| fzf --prompt=' crate> '
) || return
echo " https://docs.rs/$dep " | xdg-open -
}
Docker compose service manager
Lists docker-compose services and runs up/down/logs on selected.
fdcompose () {
local svc action
svc=$(
docker compose ps --services \
| fzf --prompt=' dc svc> '
) || return
action=" ${1:- logs} "
docker compose " $action " " $svc "
}
LSP language server status
Shows running LSP servers (via lsof) and restarts selected one.
flsp () {
local server
server=$(
lsof -i -P -n | grep -i lsp \
| awk ' {print $1" "$2" "$NF}' \
| fzf --prompt=' lsp> '
) || return
pid=$( echo " $server " | awk ' {print $2}' )
kill -9 " $pid " && echo " Restart $server LSP"
}
Fuzzy pick from Makefile targets and runs make <target>.
fmake () {
local target
target=$(
make -qp | awk -F' :' ' /^[a-zA-Z0-9][^\$#\/:=()]{1,}/ {print $1}' \
| grep -v ' ^:' | sort | uniq \
| fzf --prompt=' make> '
) || return
make " $target "
}
Searches through node_modules and shows package.json + version.
fnodemod () {
local pkg
pkg=$(
fd package.json node_modules \
| sed ' s|.*/node_modules/||; s|/package.json||' \
| fzf --prompt=' npm pkg> ' \
--preview ' jq .name,jq .version {}/package.json'
) || return
cd " node_modules/$pkg " && npm list .
}
Lists git worktrees and switches to selected one.
fgworktree () {
local wt
wt=$(
git worktree list \
| fzf --prompt=' worktree> '
) || return
cd " $( echo " $wt " | awk ' {print $1}' ) "
}
TypeScript declaration picker
Finds .d.ts files and opens them for type inspection.
ftsd () {
local decl
decl=$(
fd --extension d.ts . \
| fzf --prompt=' d.ts> ' \
--preview ' bat --language=typescript {}'
) || return
" ${EDITOR:- vim} " " $decl "
}
Pre-commit hook inspector
Shows git pre-commit hooks and their contents.
fprecommit () {
local hook
hook=$(
ls -la .git/hooks/pre-commit* 2> /dev/null \
| fzf --prompt=' hook> ' \
--preview ' cat {}'
) || return
" ${EDITOR:- vim} " " $hook "
}
Coverage report navigator
Fuzzy search through coverage reports (lcov/simplecov) and jumps to uncovered lines.
fcoverage () {
local file
file=$(
rg -g ' *.lcov,*.json' ' UNCOVERED\|Missed' coverage/ \
| fzf --prompt=' coverage> '
) || return
" ${EDITOR:- vim} " " $( echo " $file " | cut -d: -f1) "
}
Open recent project directories
Keep a simple text file with one project path per line (e.g. ~/.projects) and jump into one via fzf.
fproj () {
local projfile=" ${PROJECT_LIST:- $HOME / .projects} "
[[ -f " $projfile " ]] || { echo " No project list at $projfile " ; return 1; }
local dir
dir=$(
cat " $projfile " \
| fzf --prompt=' project> ' --preview ' ls -la {} 2>/dev/null'
) || return
cd " $dir " || return
}
Jump to git repositories under a root
Scan a development root (e.g. ~/dev) for git repos and cd into the selected one.
frepo () {
local root=" ${DEV_ROOT:- $HOME / dev} "
local dir
dir=$(
fd . " $root " --type d --hidden --exclude ' .git' -d 4 2> /dev/null \
| while read -r d; do
[[ -d " $d /.git" ]] && echo " $d "
done \
| fzf --prompt=' repo> ' --preview ' git -C {} status -sb 2>/dev/null'
) || return
cd " $dir " || return
}
Search TODO / FIXME comments across code
Search for TODO/FIXME markers, preview their context, and jump to the file/line in your editor.
ftodo_code () {
local match file line
match=$(
rg --no-heading --line-number ' TODO|FIXME' . 2> /dev/null \
| fzf --ansi --prompt=' todo/fixme> ' \
--preview ' bat --style=numbers --color=always {1} --line-range {2}:+20'
) || return
file=$( echo " $match " | cut -d: -f1)
line=$( echo " $match " | cut -d: -f2)
" ${EDITOR:- vim} " " +${line} " " $file "
}
Git file picker: tracked, untracked, ignored
Pick files from git (tracked, modified, untracked) and open in your editor.
fgit_files () {
local file
file=$(
git status --short \
| fzf --ansi --prompt=' git-files> ' \
--preview ' git diff --color=always -- {2} || bat --style=numbers --color=always {2}' \
--preview-window=right:60% \
| awk ' {print $2}'
) || return
" ${EDITOR:- vim} " " $file "
}
List files with merge conflicts and open the selected one.
fconflict () {
local file
file=$(
git diff --name-only --diff-filter=U 2> /dev/null \
| fzf --prompt=' conflicts> ' \
--preview ' bat --style=numbers --color=always {}'
) || return
" ${EDITOR:- vim} " " $file "
}
Interactive stash browser
Browse git stashes with preview and apply/pop/show the selected one.
fgstash () {
local line stash
line=$(
git stash list \
| fzf --prompt=' stash> ' \
--preview ' echo {} | cut -d: -f1 | xargs git stash show -p --color=always' \
--preview-window=right:60%
) || return
stash=$( echo " $line " | cut -d: -f1)
case " ${1:- apply} " in
apply) git stash apply " $stash " ;;
pop) git stash pop " $stash " ;;
show) git stash show -p " $stash " ;;
* ) echo " Usage: fgstash [apply|pop|show]" ;;
esac
}
List git worktrees and change directory to the selected one.
fgworktree () {
local wt
wt=$(
git worktree list 2> /dev/null \
| fzf --prompt=' worktree> '
) || return
cd " $( echo " $wt " | awk ' {print $1}' ) " || return
}
List Makefile targets and run the selected one via make.
fmake () {
local target
target=$(
make -qp 2> /dev/null \
| awk -F' :' ' /^[a-zA-Z0-9][^$#\/:=()]*:/{print $1}' \
| sort -u \
| fzf --prompt=' make> '
) || return
make " $target "
}
Run package.json scripts (npm / pnpm / yarn)
Pick a script from package.json and run it with your preferred package manager.
fpkg_script () {
local mgr script
mgr=" ${PKG_MGR:- npm} "
script=$(
jq -r ' .scripts | keys[]' package.json 2> /dev/null \
| fzf --prompt=' pkg script> ' \
--preview ' jq -r ".scripts[\"{}\"]" package.json'
) || return
case " $mgr " in
npm) npm run " $script " ;;
pnpm) pnpm run " $script " ;;
yarn) yarn " $script " ;;
* ) " $mgr " " $script " ;;
esac
}
System‑wide ripgrep search with file picker
Search for a pattern across the repo, pick a match, and open it at the matching line.
frg_open () {
local query match file line
query=" $1 "
if [[ -z " $query " ]]; then
read -rp " Search pattern: " query
fi
match=$(
rg --color=always --line-number --no-heading " $query " . 2> /dev/null \
| fzf --ansi --prompt=" rg: $query > " \
--preview ' bat --style=numbers --color=always {1} --line-range {2}:+20'
) || return
file=$( echo " $match " | cut -d: -f1)
line=$( echo " $match " | cut -d: -f2)
" ${EDITOR:- vim} " " +${line} " " $file "
}
Switch tmux sessions via fzf
Pick a tmux session and attach to it (or create one if none exists).
ftmux () {
if ! command -v tmux > /dev/null 2>&1 ; then
echo " tmux not installed"
return 1
fi
local session
session=$(
tmux list-sessions -F ' #S' 2> /dev/null \
| fzf --prompt=' tmux> '
)
if [[ -z " $session " ]]; then
tmux new-session
else
tmux attach-session -t " $session "
fi
}
Keep your dotfiles in ~/.dotfiles (or similar) and use fzf to open them quickly.
fdot () {
local root=" ${DOTFILES_ROOT:- $HOME / .dotfiles} "
local file
file=$(
fd . " $root " --type f 2> /dev/null \
| fzf --prompt=' dotfile> ' \
--preview ' bat --style=numbers --color=always {}'
) || return
" ${EDITOR:- vim} " " $file "
}