<!--
  canonical-url: https://www.avonture.be/blog/linux-fzf-introduction
  published:     2024-03-30
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# Introduction to fzf - Fuzzy Finder

> Master the Linux command line with fzf (Fuzzy Finder). Discover its powerful fuzzy search for history (CTRL+R) and file selection (CTRL+T) to boost your productivity.

---
![Introduction to fzf - Fuzzy Finder](/img/v2/linux_tips.webp)

**TL;DR**

This article introduces `fzf` (Fuzzy Finder), which upgrades the console's `CTRL`+`R` history search with fuzzy, any-order-letter matching, and adds `CTRL`+`T` for interactively fuzzy-selecting one or more files (usable with commands like `rm` or `head`) and `ALT`+`C` to jump into a folder.

As you probably know, `CTRL`+`R` in the console will give you access to your HISTORY i.e. you'll retrieve the list of the commands you previously typed in your console. Just like using `UP` or `DOWN` keys but with a very small search engine.

And it makes the job but, honestly, this is really basic, no?

The Fuzzy Finder (aka `fzf`) command line utility will explode the possibilities linked to searching in the history but, in reality, this is just one of the consequences of installing fzf, which is so much more powerful.

*Three articles on this blog build directly on `fzf`: [FZF + ripgrep: Interactive Code Search with Live Preview](/blog/fzf-ripgrep), [Master your ssh command and select the host from a list](/blog/ssh-with-fuzzy-finder) and [ZSH Functions - Customizing Your Shell for Docker Management](/blog/zsh-docker-functions).*

Let's have a look.

By pressing `CTRL`+`R`, you'll natively get a *reverse search* for your history. Start to type a command like `ls` in my example below and Linux will show you the last command used.

![Using CTRL-R](./images/ctrl_r.webp)

## Seeing It in Action with Docker

Before cloning anything on your machine, spin up a throwaway container with `fzf` already wired
in and a small playground already built.

> **Why Docker first?:**
>
> The Dockerfile below installs `fzf`, enables its key bindings in `bash`, pre-seeds a shell history
> (so `CTRL`+`R` has something to search) and creates a handful of log files and
> nested folders (so `CTRL`+`T` and `ALT`+`C` have something to
> browse). Nothing to prepare yourself.

**Dockerfile**

```docker
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        bash \
        git \
        curl \
        ca-certificates && \
    rm -rf /var/lib/apt/lists/*

# Install fzf and wire its key bindings + fuzzy completion into bash
RUN git clone --depth 1 https://github.com/junegunn/fzf.git /root/.fzf && \
    /root/.fzf/install --key-bindings --completion --no-update-rc && \
    echo '[ -f ~/.fzf.bash ] && source ~/.fzf.bash' >> /root/.bashrc

# Build a playground: log files to fuzzy-select with Ctrl+T, nested folders
# to jump into with Alt+C
RUN mkdir -p /root/demo/logs /root/demo/scripts /root/demo/nested/deep/folder && \
    for i in 1 2 3 4; do \
        printf 'run%s.sh.log — line 1\nline 2\nline 3\nline 4\nline 5\n' "$i" \
            > /root/demo/logs/run"$i".sh.log; \
    done && \
    touch /root/demo/scripts/deploy.sh /root/demo/scripts/backup.sh

# Pre-seed bash history so Ctrl+R has something real to fuzzy-search: type
# "github" and the git-clone line below turns up, exactly like the article's
# own screenshots.
RUN printf '%s\n' \
    'cd /root/demo' \
    'ls -la' \
    'git clone https://github.com/junegunn/fzf-git.sh.git' \
    'docker compose up --detach' \
    'head -n 5 logs/run1.sh.log' \
    > /root/.bash_history

WORKDIR /root/demo

# Signal clearly that this shell runs inside a container
RUN echo "PS1='\n\e[0;33m🐳 \e[0;36m\$(whoami)\e[0m \w # '" >> /root/.bashrc

CMD ["/bin/bash", "-i"]
```

Build and run it:

```bash
$ docker build -t fzf-demo .
[+] Building 24.6s (9/9) FINISHED
✔ exporting to image
$ docker run --rm -it fzf-demo
🐳 root ~/demo #
```

You're already in `/root/demo`. Try the three shortcuts right away:

* Press `CTRL`+`R` and type `github` — the pre-seeded `git clone
  https://github.com/junegunn/fzf-git.sh.git` line turns up, exactly like the screenshots below.
* Press `CTRL`+`T` and type `run` — the four `run*.sh.log` files in `logs/`
  appear, filtered live as you type.
* Press `ALT`+`C` to fuzzy-jump into `nested/deep/folder` without typing a
  single `cd`.
* Run `fzf -m | xargs head -n 5`, filter on `run`, select two or three files with `TAB`,
  press `ENTER` — the multi-select scenario from the "Getting a list of files" section
  below, ready to try without deleting anything real.

## Installation

The installation of `fzf` is simple, you just need to run `git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf && ~/.fzf/install` and answer by `Y`es to two questions.

When done, close and restart a new console (so Linux will take into account changes made to your configuration files).

## Playing with the history

Now, press `CTRL`+`R` again and you'll get something much better:

![Using CTRL-R once FZF has been installed](./images/ctrl_r_fzf.webp)

Just like previously, start to type the command you would like to retrieve; in my case, I know, once, I've tried to install a repo from github but don't remember the instruction so I'll start to type `github`:

![Filtering on github](./images/ctrl_r_github.webp)

If you look at the image, you'll see that `fzf` is searching for the `g`, `i`, `t`, `h`, `u` and `b` letters, not for the word.  So the first line has the word `git` then there is a `h` somewhere and the word `public` so, `ub` are there.

This is handy since you don't need to remember the exact order of words in your previous command line, just type words in any particular order.

For me, just for this usage, the installation of `fzf` is already fully justified.

## Getting a list of files

Imagine you wish to display the content of a file so, you start to type `cat` and, oh, you don't remember well the exact filename. By pressing `CTRL`+`T` you'll get a list of files from the current directory and subdirectories:

![Using CTRL-T](./images/ctrl_t.webp)

You can navigate using `UP` and `DOWN` but also type some letters to filter the list.

On my machine, by starting to type `doc` I'll get the list of files, anywhere in the tree structure, with `doc`. I can continue and type `doc.md` to force the presence of letters `md` and thus, in my case, search for articles about Docker and having the extension `.md`.

![Using CTRL-T and filtering on doc](./images/ctrl_t_doc.webp)

Such use is handy since I don't first need to retrieve the exact name of the file I wanted to use. In this example it was for `cat` but, of course, it works for anything: type your command and press `CTRL`+`T`.

> **`**` followed by <kbd>TAB</kbd> is an alias:**
>
> Not sure it's something to remember, but `cat **` followed by `TAB` will work just like pressing `CTRL`+`T`. Here, `**` will be expanded to `CTRL`+`T`.

Here, using `CTRL`+`T`, you'll be able to select just one file. Imagine you wish to select more than one? For instance, you wish to run `rm` to remove more than one file.

By running `fzf -m | xargs rm` followed by `ENTER`, Fuzzy Finder will display the list of all files present in the current directory. Like previously, I can start to type and filter the list. In my example below, I've typed `run.sh.log` to retrieve logs and, in the list, I press `TAB` to select four files (see the red `>` character at the left). Then I press `ENTER` to validate my choice and ... files have been removed since my command was `rm`.

![Removing several files](./images/rm_several_files.webp)

If I rerun the same command and the same filter, I can see that I now only retrieve three files, the ones I didn't select. The `rm` has worked correctly.

![Removing several files, second run](./images/rm_several_files_bis.webp)

Now, instead of killing files, I'll just display the first five lines of each selected file: To do this, I'll execute `fzf -m | xargs head -n 5`. Filter on `run.sh.log` again, select the three files using `TAB`, press `ENTER` to validate my choice and tadaaa:

![Selecting several files](./images/head_several_files.webp)

## Keybindings

| Shortcut   | Description                                                                                                                                                                                             |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Ctrl`+`R` | Show the list of commands you've previously typed (your history thus), allow you to select a command by pressing ENTER.                                                                                 |
| `Ctrl`+`T` | Display the list of files in the current directory and sub-folders, allow you to select one or more files (just press TAB for a multiple selection) then press ENTER to return the list to the console. |
| `Alt`+`C`  | Get the list of folders recursively of the current directory, allow you to select one and by pressing ENTER, fzf will jump in that folder.                                                              |

Once these basics feel natural, fzf really shines when combined with other tools: see [FZF + ripgrep](/blog/fzf-ripgrep) for interactive code search with a live preview, or [Master your ssh command and select the host from a list](/blog/ssh-with-fuzzy-finder) to stop typing hostnames from memory. If you specifically want richer history search — timestamps, exit codes, and working directory per command — Atuin is worth a look: it replaces the flat `~/.bash_history` file with a SQLite database while keeping the same `Ctrl+R` shortcut.
