fzf-tools is a zsh plugin that makes using the command line easier and faster. It adds interactive selection with fzf, allowing you to quickly find files, run commands from your history, execute different types of scripts, and browse through git commits. This tool helps you work more efficiently and straight up get things done quicker.

You can pretty much pipe anything you want to fzf and it will show you the results in a nice interactive list. It's a great tool to have in your tool belt.



Install

To install fzf you can use brew or apt-get like so:

Package ManagerLinux DistributionCommand
APKAlpine Linuxsudo apk add fzf
APTDebian 9+/Ubuntu 19.10+sudo apt install fzf
Condaconda install -c conda-forge fzf
DNFFedorasudo dnf install fzf
NixNixOS, etc.nix-env -iA nixpkgs.fzf
PacmanArch Linuxsudo pacman -S fzf
pkgFreeBSDpkg install fzf
pkginNetBSDpkgin install fzf
pkg_addOpenBSDpkg_add fzf
PortageGentooemerge --ask app-shells/fzf
Spackspack install fzf
XBPSVoid Linuxsudo xbps-install -S fzf
ZypperopenSUSEsudo zypper install fzf

Then add the following line to your shell configuration file.

bash

# Set up fzf key bindings and fuzzy completion eval "$(fzf --bash)"

zsh

# Set up fzf key bindings and fuzzy completion source <(fzf --zsh)

fish

# Set up fzf key bindings fzf --fish | source

Search syntax

Unless otherwise specified, fzf starts in "extended-search mode" where you can type in multiple search terms delimited by spaces. e.g. ^music .mp3$ sbtrkt !fire

TokenMatch typeDescription
sbtrktfuzzy-matchItems that match sbtrkt
'wildexact-match (quoted)Items that include wild
'wild'exact-boundary-match (quoted both ends)Items that include wild at word boundaries
^musicprefix-exact-matchItems that start with music
.mp3$suffix-exact-matchItems that end with .mp3
!fireinverse-exact-matchItems that do not include fire
!^musicinverse-prefix-exact-matchItems that do not start with music
!.mp3$inverse-suffix-exact-matchItems that do not end with .mp3

If you don't prefer fuzzy matching and do not wish to "quote" every word, start fzf with -e or --exact option. Note that when --exact is set, '-prefix "unquotes" the term.


Useful FZF Functions

Here comes the real meaty part: The functions. These are ways to extend fzf to make using the terminal better, faster, and more efficient. Take a dive:


# Another fd - cd into the selected directory # This one differs from the above, by only showing the sub directories and not # showing the directories within those. fd() { DIR=`find * -maxdepth 0 -type d -print 2> /dev/null | fzf --height 40% \ --border sharp \ --layout reverse \ --prompt '∷ ' \ --pointer ▶ \ --marker ⇒ ` \ && cd "$DIR" }

This is probably my most used fzf command. It allows you to cd into a directory and display it in a fzf dropdown list. It will then cd into the selected directory. It's just such a breeze to type fuzzy search directories like fd, hit enter, type incomplete strings like apiutilrou and it presents you with the exact directory you and upon hitting enter again it will cd into the directory.


# preview files fds() { find . -type f | fzf --style full \ --preview 'fzf-preview.sh {}' \ --bind 'focus:transform-header:file --brief {}' }

I use this one quite a bit. It allows you to search for files and display them in a fzf dropdown list. It will then preview the selected file in the terminal.


# fdr - cd to selected parent directory fdr() { local declare dirs=() get_parent_dirs() { if [[ -d "${1}" ]]; then dirs+=("$1"); else return; fi if [[ "${1}" == '/' ]]; then for _dir in "${dirs[@]}"; do echo $_dir; done else get_parent_dirs $(dirname "$1") fi } local DIR=$(get_parent_dirs $(realpath "${1:-$PWD}") | fzf --height 40% \ --border sharp \ --layout reverse \ --prompt '∷ ' \ --pointer ▶ \ --marker ⇒ --tac) cd "$DIR" }

Pick and choose the parent directory of the selected directory. This one is useful when you have a lot of nested directories and you want to quickly jump to the parent directory.


# fcd - cd into the directory of the selected file fcd() { local file local dir file=$(fzf --height 40% \ --border sharp \ --layout reverse \ --prompt '∷ ' \ --pointer ▶ \ --marker ⇒ +m -q "$1") && dir=$(dirname "$file") && cd "$dir" }

This one takes the selected file and cds into the directory of the file. It will show you the files in a fzf dropdown list and you can select one and it will cd into the directory of the selected file.


# fh - repeat command history fh() { eval $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf --height 40% \ --border sharp \ --layout reverse \ --prompt '∷ ' \ --pointer ▶ \ --marker ⇒ +s --tac | sed -E 's/ *[0-9]*\*? *//' | sed -E 's/\\/\\\\/g') }
# keybind to up bindkey "${key[Up]}" fzf-history-widget

You can bind the fzf-history-widget to a key of your choice. I think by default it's ctrl + r but I prefer just using the up arrow key since i'm going to cycle the through list anyway or even use the mousewheel. Not trying to use vim commands here.


# fkill - kill processes - list only the ones you can kill. fkill() { local pid if [ "$UID" != "0" ]; then pid=$(ps -f -u $UID | sed 1d | fzf --height 40% \ --border sharp \ --layout reverse \ --prompt '∷ ' \ --pointer ▶ \ --marker ⇒ -m | awk '{print $2}') else pid=$(ps -ef | sed 1d | fzf --height 60% \ --border sharp \ --layout reverse \ --prompt '∷ ' \ --pointer ▶ \ --marker ⇒ -m | awk '{print $2}') fi if [ "x$pid" != "x" ] then echo $pid | xargs kill -${1:-9} fi }

Search and destry. Kill that annoying process that's hanging up.

# kill processes - list only the ones you can kill. smite() { setopt LOCAL_OPTIONS ERR_RETURN PIPE_FAIL local opts=( -I ) if [[ $1 == '-a' ]]; then opts=() elif [[ -n $1 ]]; then print >&2 'usage: smite [-a]' return 1 fi fc -l -n $opts 1 | \ fzf --no-sort --tac --multi | \ while IFS='' read -r command_to_delete; do printf 'Removing history entry "%s"\n' $command_to_delete local HISTORY_IGNORE="${(b)command_to_delete}" fc -W fc -p $HISTFILE $HISTSIZE $SAVEHIST done }

I use this one if I ever end up putting a password in the history by accident. smite by itself will show you local history of that shell session. Passing the -a flag will show you all history. Hitting enter will remove the selected history entry. After you hit enter you will be greeted with the result as such:

Removing history entry "smite"

# fbr - checkout git branch (including remote branches) fbr() { local branches branch branches=$(git branch --all | grep -v HEAD) && branch=$(echo "$branches" | fzf-tmux -d $(( 2 + $(wc -l <<< "$branches") )) +m) && git checkout $(echo "$branch" | sed "s/.* //" | sed "s#remotes/[^/]*/##") }

Checkout a git branch. It will show you the branches in a fzf dropdown list and you can select one amd after hitting enter it will git checkout into the selected branch. Useful, but I use lazygit instead.


# fcoc - checkout git commit fcoc() { local commits commit commits=$(git log --pretty=oneline --abbrev-commit --reverse) && commit=$(echo "$commits" | fzf --tac +s +m -e) && git checkout $(echo "$commit" | sed "s/ .*//") }

This one is usefeul as it allows you to checkout a git commit. It will show you the commits in a fzf dropdown list and you can select one and it will checkout the selected commit. I've since abandoned this in favor of lazygit which is an overall better git experience and visual interface.


# fshow - git commit browser (& view commit contents) fshow() { git log --graph --color=always \ --format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" | fzf --ansi --no-sort --reverse --tiebreak=index --bind=ctrl-s:toggle-sort \ --bind "ctrl-/:execute: (grep -o '[a-f0-9]\{7\}' | head -1 | xargs -I % sh -c 'git show --color=always % | less -R') << 'FZF-EOF' {} FZF-EOF" }

This one is a git commit browser. It will show you the commits in a fzf dropdown list and you can select one and it will show you the commit contents. It reverses the order of the commits so that the most recent commits are at the top of the list and color coordinates the commits.

Again, I've since abandoned this in favor of lazygit.


# delete file from fzf rmf() { if [[ "$#" -eq 0 ]]; then local files files=$(find . -maxdepth 1 -type f | fzf --multi --height 40% \ --border sharp \ --layout reverse \) echo $files | xargs -I '{}' rm {} #we use xargs to capture filenames with spaces in them properly else command rm "$@" fi }

This one lets you delete a file or directory from a fzf dropdown list. Helpful when you don't remember the exact name of a file you want to delete or when you have a lot of files and you don't want to type them all out.


# aliases aliases() { CMD=$( ( (alias) (functions | grep "()" | cut -d ' ' -f1 | grep -v "^_" ) ) | fzf | cut -d '=' -f1 ); eval $CMD }

This one is a simple alias that will search for aliases and functions and display them in a fzf dropdown list. It will then evaluate the selected alias or function. Useful for quickly finding aliases or functions you want to use. I didn't want to screenshot this one cause it contains stuff I don't want to show.


# search env vars envs() { local out out=$(env | fzf) echo $(echo $out | cut -d= -f2) }

envs allows you to search for environment variables and display them in a fzf dropdown list. It will then echo the selected environment variable.


# like normal z when used with arguments but displays an fzf prompt when used without. unalias z 2> /dev/null z() { [ $# -gt 0 ] && _z "$*" && return cd "$(_z -l 2>&1 | fzf --height 40% --nth 2.. --reverse --inline-info +s --tac --query "${*##-* }" | sed 's/^[0-9,.]* *//')" }

If you use z with arguments it will behave like normal z and will show you a fzf prompt. If you don't use any arguments it will show you a fzf prompt and then use the selected directory as the new working directory. Super useful when your projects folder is filled to the brim with stuff.


fzf is super powerful, very performant, and very easy to use, especially once you get the hang of it. I've been using it for years and I feel lost without it now when using other's machines. There's tons of plugins for fzf and a fairly large community of users.

I just went into a few of my favorite functions, but the rabbit hole goes much deeper. For exmaple using fzf as a ripgrep launcher or image previewer.If you want to dive deeper, there's some links definetly worth checking out below.

Hope you get some use out of these.


Resources

Further Reading


Credits