Blog Post Publish Date: 2024/04/28


fzf: Life is Too Short for Pipe Grep#

This blog post outlines the advantages of the fzf (Fuzzy Finder CLI) and explains how to configure its Zsh widgets.

Searching in the Terminal - Tedious Task#

Searching for items in the terminal is a trivial activity, but can be a boring and tedious task. I will list three cases to exemplify it: searching command history; files and directories; and command output.

Usually, users press the arrow up/down to recall last commands executed. The most used shells implemented an interactive search tool called by CTRL + R shortcut. In Zsh, it is implemented as reverse-i-search, which works well, but you need to press the shortcut again to see the next results. This behavior can be a problem if you need to recall commands executed multiple times with different arguments.

When it is necessary to find some file or directory, the most common method adopted is to use $ find. It works well when you know exactly the item that you need to find, but not so good when you only know some partials of the path.

Lastly, I can mention searching elements in command outputs. The most adopted method for this case is to use matching expressions within a command output using $ command | grep. When the same problem of finding files and directories previously described is true for this. Depending on the case, it is necessary to execute grep multiple times until you find the elements that you need.

This tedious task prompted me to research better alternatives for interactive searching.

Fuzzy Finders - The Solution#

After a few days of research, I found an amazing project that solves my problems: fzf — command-line fuzzy finder. It is a cross-platform Fuzzy Finder command-line written in Go. But, what is Fuzzy Finder?

Fuzzy Finder is a search tool category for providing a quickly and flexibly way to find files, directories, or other elements, even when they don’t remember the precise names. It employs fuzzy matching to find results based on partial words, erratic characters, or typographical errors.

fzf filters the list of items as you type. This makes your searching much more efficient, avoiding $ command | grep execution. It can receive output of the other commands for interative searching, for instance you can execute $ kgp -A | fzf to find pods in a Kubernetes cluster. In addition, it has some predefined shells widgets to search files, directories, and search history commands.

The next section will explain how to configure these widgets in Zsh.

There are widgets available for Fish, Bash, Zsh. You can check the available implementations in github.com/junegunn/fzf/shell

How to Configure fzf Widgets in Zsh#

The first step is to install fzf. There are some distinct ways to install it described in fzf — installation section. Depending on the package manager on your workstation, the version available can be older. Thus, I will download and install the latest version available in GitHub Releases section.

setopt INTERACTIVE_COMMENTS

# download the latest version (in a moment of the blog publish date, the latest version is 0.50.0)
curl -L https://github.com/junegunn/fzf/releases/latest/download/fzf-0.50.0-linux_amd64.tar.gz > fzf-0.50.0-linux_amd64.tar.gz

# uncompress the donwloaded file to access the fzf binary
tar -xzvf fzf-0.50.0-linux_amd64.tar.gz

# check fzf binary execution
./fzf --help

# move the binary to a folder present in your path
mv fzf /usr/local/bin/

The fzf use the command $ find behind the scenes as engine to search directories and files. You can change it. In this example, I will install and configure the fd-find, a fast and user-friendly alternative to $ find.

# In Linux workstation with dnf package manager
dnf install fd-find

# In MacOs workstation with homebrew package manager
brew install fd

For file content preview, I will install and configure bat, a $ cat clone with syntax highlighting.

# In Linux workstation with dnf package manager
dnf install bat

# In MacOs workstation with homebrew package manager
brew install bat

For directory and sub directories preview, I will install and configure tree, a utility which recursively displays the contents of directories in a tree-like format.

# In Linux workstation with dnf package manager
dnf install tree

# In MacOs workstation with homebrew package manager
brew install tree

Now, it is necessary load the Shell widgets. For this, you can get the widget setup source-code given the shell name as argument (--zsh, --fish, --bash). In this case, I will setup the widgets for Zsh.

# Set up fzf key bindings and fuzzy completion (only available in 0.48.0 or later)
eval "$(fzf --zsh)"

Run the following command to check the keybindings are correctly loaded.

bindkey -a | grep fzf

# output expected:
#
# "^R" fzf-history-widget  # CTRL + R: reverse history search
# "^T" fzf-file-widget     # CTRL + T: search files
# "^[c" fzf-cd-widget      # ALT  + C: search directories

You can customize the widgets fzf parameters through environment variables described in fzf — environment variables. Each widget has its own environments variables you can customize based on your needs. Take a look at following code block for an example of the how to parametrize fzf widgets.

# fzf parameters used in all widgets - configure layout and wrapped the preview results (useful in large command rendering)
export FZF_DEFAULT_OPTS="--height 100% --layout reverse --preview-window=wrap"

# CTRL + R: put the selected history command in the preview window - "{}" will be replaced by item selected in fzf execution runtime
export FZF_CTRL_R_OPTS="--preview 'echo {}'"

# ALT + C: set "fd-find" as directory search engine instead of "find" and exclude "venv|virtualenv|.git" of the results during searching
export FZF_ALT_C_COMMAND="fd --type directory --exclude venv --exclude virtualenv --exclude .git"

# ALT + C: put the tree command output based on item selected
export FZF_ALT_C_OPTS="--preview 'tree -C {}'"

# CTRL + T: set "fd-find" as search engine instead of "find" and exclude "venv|virtualenv|.git" for the results
export FZF_CTRL_T_COMMAND="fd --exclude venv --exclude virtualenv --exclude .git"

# CTRL + T: put the file content if item select is a file, or put tree command output if item selected is directory
export FZF_CTRL_T_OPTS="--preview '[ -d {} ] && tree -C {} || bat --color=always --style=numbers {}'"

The next section will present widgets executions preview.

You can check my ~/.zshrc file content in the following link. It contains my personal fzf parametrization: c-neto/ansible-configure-fedora

Results#

  • fzf-history-widget executed by CTRL + R.

fzf-history-widget execution print

  • fzf-cd-widget executed by ALT + C.

fzf-cd-widget execution print

  • fzf-file-widget executed by CTRL + T.

fzf-file-widget execution print

  • Searching pods in Kubernetes Cluster with $ kgp -A | fzf execution.

find pods with fzf print

Conclusion (Author Opinion)#

My research for improvements in searching history commands has yielded results that exceed my expectations. fzf opened my mind to understand what is Fuzzy Finder and it purpose. It not limited to be command history searching only, it is a tool that you can search anything. It is really nice!

Certainly, I think it is a much better alternative to command history search widget default in Zsh, Bash, and Fish (which already has a good history search widget).

The interactive searching are blazingly fast and customization expanding the possibilities based on your needs. The productivity which fzf provides is really awesome! In a fact, that its justifies more than 59K stars in your GitHub repo.

I approve and recommend!