Thursday, March 20, 2025

Integrating fzf with the bash shell

command-line fuzzy finder(fzf) Shell Integration

Introduction

fzf is a highly customizable command-line fuzzy finder. Per the project's README:

It's an interactive filter program for any kind of list; files, command history, processes, hostnames, bookmarks, git commits, etc. It implements a "fuzzy" matching algorithm, so you can quickly type in patterns with omitted characters and still get the results you want.

Reviewing my notes, I realize that I'm barely scratching the surface of how fzf can be used and customized. For now, I'm going to outline how I've set up fzf for daily usage in the bash shell.

  • Installation and upgrading fzf
  • What key combinations can be used to fuzzy search through files and directories.
  • Customizing the finder display

Installation

This article assumes an fzf installation directly from GitHub and using the bash shell. For other installation methods and shell-dependent commands, refer to the fzf installation documentation.

  1. git clone --depth 1 https://github.com/junegunn/fzf.git ~/.config/fzf ~/.config/fzf/install

    Note that this is slightly different than the recommended install command. When I am able, I try to move installations off of the root of my home directory and, preferably, place them in the ~/.config/ directory.

  2. The installation script will prompt for three configuration options:

    • Do you want to enable fuzzy auto-completion? ([y]/n)
    • Do you want to enable key bindings? ([y]/n)
    • Do you want to update your shell configuration files? ([y]/n)

    Unless there is a compelling reason to do otherwise, accept the default choices.

  3. Here, the installation places the file .fzf.bash, which sets up the key bindings and completions, in the root of the home directory and modifies the .bashrc to source this file on startup. Again, I make some modifications to place this file in the ~/.config/fzf/ directory:
    • mv ~/.fzf.bash ~/.config/fzf/fzf.bash
    • Changes to fzf.bash:
      # Setup fzf
      if [[ ! "$PATH" == */home/kbowen/.config/fzf/bin* ]]; then
      PATH="${PATH:+${PATH}:}/home/kbowen/.config/fzf/bin"
      fi
      # Setup key bindings and shell completion
      eval "$(fzf --bash)"
    • Add the following line to .bashrc:
      # Set up fzf key bindings and fuzzy completion
      [ -f ~/.config/fzf/fzf.bash ] && source "$HOME/.config/fzf/fzf.bash"
  4. For reference, the keybindings and completion in fzf are provided by two files. In the installation directly from git, these files are located here:

      ~/.config/fzf/shell/key-bindings.bash
      ~/.config/fzf/shell/completion.bash
    

    Modifying the default installation is not required, just a personal preference of mine.

Upgrading fzf

fzf is actively developed and updated relatively frequently. Check for updates regularly with the command:

  • cd ~/.config/fzf && git pull && ./install
    For convenience, I've created an alias for this in my .bash_aliases file:
  • alias fzf_update='cd ~/.fzf && git pull && ./install && cd -'

Keybindings with fzf

Key Combination Description
CTRL-t Search all files and subdirectories of the working directory, and output the selection to STDOUT.
ALT-c Search all subdirectories of the working directory, and run the command cd with the output as argument.
CTRL-r Search your shell history, and output the selection to STDOUT.

CTRL-r is my most frequently used fzf command. Generally, when I want to run the previous command or the one before that, I'll use !! or !-2. After that, I don't keep the commands in my memory and using CTRL-r has become a great time-saver, for me.

Using CTRL-t is useful if you want to locate and run a command on a file. For example, to edit a file, type vim at the command line, then press the key combination <Ctrl-t> to perform a search. Finally, press <Enter> twice to open the file inside vim.

ALT-c is used for searching for and moving into a subdirectory.

Using the finder

Using any of these three key combinations will bring up a finder that will display a list(either of files, commands, or directories) and filter the results depending on the text entered.

Movement within the finder display

  • CTRL-K / CTRL-J (or CTRL-P / CTRL-N) to move cursor up and down
  • Enter key to select the item, CTRL-C / CTRL-G / ESC to exit
  • On multi-select mode (-m), TAB and Shift-TAB to mark multiple items
  • Emacs style key bindings
  • Mouse: scroll, click, double-click; shift-click and shift-scroll on multi-select mode

Customizing the finder display

By default, the finder will display a window without a file preview with the filter command line at the bottom of the display. With a little bit of tinkering, I've build a display with a preview window to show file contents as well as look aesthetically pleasing to me. For this, I needed to modify the FZF_DEFAULT_OPTS environment variable and add it to the .bashrc file:

# Set the fzf display layout
export FZF_DEFAULT_OPTS=" \
--multi \
--height=50% \
--margin=5%,2%,2%,5% \
--reverse \
--preview='(highlight -O ansi {} || bat {}) 2> /dev/null | head -n 50' \
--preview-window=45%,border-double --border=double \
--prompt='▶' \
--pointer='→' \
--marker='✗' \
--header='CTRL-c or ESC to quit' \
--color='dark,fg:magenta,border:cyan'"

See the fzf README for details on customizing the look of the display filter.