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 Manager | Linux Distribution | Command |
---|---|---|
APK | Alpine Linux | sudo apk add fzf |
APT | Debian 9+/Ubuntu 19.10+ | sudo apt install fzf |
Conda | conda install -c conda-forge fzf | |
DNF | Fedora | sudo dnf install fzf |
Nix | NixOS, etc. | nix-env -iA nixpkgs.fzf |
Pacman | Arch Linux | sudo pacman -S fzf |
pkg | FreeBSD | pkg install fzf |
pkgin | NetBSD | pkgin install fzf |
pkg_add | OpenBSD | pkg_add fzf |
Portage | Gentoo | emerge --ask app-shells/fzf |
Spack | spack install fzf | |
XBPS | Void Linux | sudo xbps-install -S fzf |
Zypper | openSUSE | sudo 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
Token | Match type | Description |
---|---|---|
sbtrkt | fuzzy-match | Items that match sbtrkt |
'wild | exact-match (quoted) | Items that include wild |
'wild' | exact-boundary-match (quoted both ends) | Items that include wild at word boundaries |
^music | prefix-exact-match | Items that start with music |
.mp3$ | suffix-exact-match | Items that end with .mp3 |
!fire | inverse-exact-match | Items that do not include fire |
!^music | inverse-prefix-exact-match | Items that do not start with music |
!.mp3$ | inverse-suffix-exact-match | Items 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 cd
s 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
- fzf
- Releases
- Examples
- Advanced Examples
- Theme Generator
- Setting up for Shell Integration
- "Using fzf as interactive Ripgrep launcher"
- Related Projects
- fzf.vim
- nvim.fzf
Further Reading
- Getting Started
- Fast Project Finding With fzf
- Introduction to fzf command
- A Practical Guide to fzf: Building a File Explorer
- Why you should be using fzf, the command line fuzzy finder
- How to Install and Use fzf on Linux
- Find anything you need with fzf, the Linux fuzzy finder tool
- fzf - Man Page
- Supercharge Your Zsh Terminal with fzf: A Simple Guide