Faster File Search in the Terminal with fzf and ripgrep

TL;DR: The Short Version

ripgrep finds text across thousands of files in milliseconds. fzf adds an interactive picker that narrows results as you type. Used together, they replace slow grep and find pipelines with one fast search box inside your terminal. Install both tools, add two shell functions from this guide, and file search becomes the fastest part of your workflow instead of the slowest.

Why Default Search Tools Slow You Down

Every developer knows the pattern. You need one function definition buried in a project with 40,000 files. You run grep -r, wait, watch it crawl through node_modules and .git, then kill it and try find with -name instead. Minutes pass. The answer was a three-line function.

The stock tools are not broken. They were built for a different era. GNU grep scans everything you point it at, including directories you never want searched. find locates filenames but knows nothing about file contents. Chaining them with pipes works, and I used that setup for years, but each added pipe stage costs speed and readability.

Modern replacements fix the defaults. They skip hidden directories and gitignored files unless you ask otherwise. They search in parallel across CPU cores. They respect your .gitignore out of the box. The two I reach for daily are ripgrep for raw search speed and fzf for interactive picking, and the combination covers nearly every search task I hit in a workday.

ripgrep in 5 Minutes

ripgrep, invoked as rg, is a line-oriented search tool written in Rust. Its headline feature is speed: on large codebases it routinely runs 10 to 100 times faster than grep because it uses parallel directory traversal, memory maps files, and skips what you would skip anyway.

Install it with your system package manager:

# macOS
brew install ripgrep

# Ubuntu or Debian
sudo apt install ripgrep

# Fedora
sudo dnf install ripgrep

# Arch
sudo pacman -S ripgrep

Basic usage mirrors grep, minus the flags you always had to add:

# Search current directory, skip .git and ignored files
rg "TODO" .

# Case insensitive search
rg -i "usermanager" src/

# Show line numbers (on by default) with 2 lines of context
rg -C 2 "def authenticate" .

# List only filenames that contain a match
rg -l "stripe_secret" .

# Search specific file types only
rg -tpy "import requests" .
rg -Tjs "console.log" .

The flags I use most fit in a small table worth memorizing:

FlagMeaningWhen I use it
-iCase insensitiveSearching unfamiliar code with unknown casing
-lFilenames onlyFinding which files reference a secret or key
-C NN lines of contextReading a function without opening the file
-t TYPERestrict to file typeCutting noise in mixed repos
--hiddenInclude hidden filesSearching dotfiles and config
-g PATTERNCustom glob filterIncluding or excluding paths per search

Two details deserve attention. First, ripgrep honors .gitignore, .ignore, and .rgignore files, so searches stay relevant without extra flags. Second, the -g flag accepts negations like -g '!*test*', which replaces entire exclude-dir chains you used to memorize.

Colorful source code open in a dark editor while searching a large codebase in the terminal

fzf in 5 Minutes

fzf is a general-purpose fuzzy finder for the terminal. Pipe any list into it and you get a full-screen interactive picker: type characters to narrow the list, use arrow keys or Ctrl-j and Ctrl-k to move, press Enter to select. It ships with shell integration that upgrades three things you already use.

# macOS
brew install fzf

# Ubuntu or Debian
sudo apt install fzf

# Then enable shell integration (adds key bindings)
# zsh users add this line to ~/.zshrc
source <(fzf --zsh)

# bash users add this line to ~/.bashrc
eval "$(fzf --bash)"

The default key bindings change daily terminal life:

ShortcutAction
Ctrl-TPick a file or directory, path pasted to your command line
Ctrl-RFuzzy search through shell history
Alt-CFuzzy jump into a directory

Ctrl-R alone justifies the install. Instead of pressing up-arrow forty times or grepping ~/.bash_history, you tap Ctrl-R, type fragments of the command in any order, and pick the match. Fuzzy means dkr ps can match docker ps --format.

fzf also previews selections. Add a preview command and the picker shows file contents or directory trees beside the list:

# Preview files with bat, fall back to cat
export FZF_DEFAULT_OPTS="--preview 'bat --color=always {} 2>/dev/null || cat {}'"

Combine Them: Search That Updates as You Type

Here is where the pair earns its reputation. ripgrep is fast but not interactive: each new pattern means a new command. fzf is interactive but its built-in fuzzy match runs against a fixed list. The official integration pattern from the fzf author hands filtering to ripgrep and keeps fzf as the interactive front end.

The mechanism uses two fzf options. The --disabled flag stops fzf from filtering the list itself. A change event binding re-runs ripgrep on every keystroke and reloads the list with fresh results. Add this function to your shell config:

# Interactive ripgrep search with fzf front end
# Usage: rgi "search pattern"
rgi() {
  local query="$1"
  rg --column --line-number --no-heading --color=always \
    --smart-case "$query" . 2>/dev/null |
  fzf --ansi --disabled \
    --query "$query" \
    --bind "change:reload:rg --column --line-number --no-heading \
      --color=always --smart-case {q} . 2>/dev/null || true" \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2} 2>/dev/null || sed -n "{2}p" {1}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3'
}

Type rgi "authenticate" and a picker opens showing every match. Keep typing and the list refreshes through ripgrep on each keystroke. The preview pane shows the matched file with the hit line highlighted. This pattern comes straight from the fzf ripgrep integration guide by the fzf author, and I recommend reading that page once you have the basics running.

Take the result somewhere useful. The --delimiter : option splits each line into filename, line number, and column, so you can open the selection directly in your editor:

# Open selected match in Vim at the exact line
rgi "TODO" | awk -F: '{print "+"$2" "$1}' | xargs -r vim

# Open selected match in VS Code at the exact line
match=$(rgi "FIXME")
file=$(echo "$match" | cut -d: -f1)
line=$(echo "$match" | cut -d: -f2)
code --goto "$file:$line"

Close-up of program code on a monitor representing fast terminal text search with ripgrep

Tool Comparison: Pick the Right Default

Each tool below has a job where it wins. The point is not that grep is useless. The point is knowing which default to reach for so the common case stays fast.

Taskgrep / findripgrepfzf + ripgrep
Find text in a large repoSlow, needs exclude flagsYes, fastest optionYes, plus live refinement
Find a file by nameYes with findNo, content onlyYes with Ctrl-T picker
Explore unknown pattern interactivelyNoNo, one shot per runYes, reloads per keystroke
Search command historyManual history grepNot its jobYes with Ctrl-R
Scripted one-off searchYes, everywhere by defaultYes where installedNo, needs a terminal UI
Available on fresh serversYes, preinstalledNeeds installNeeds install

The last row matters for server work. I keep grep skills sharp for machines I cannot customize, and I install ripgrep everywhere I control. If you manage VPS boxes regularly, pair this setup with a solid VPS backup routine so your tuned shell config survives a rebuild.

6 Workflows I Use Every Day

Tools only pay off through habits. These six cover most of what I do between opening a terminal and opening an editor.

1. Jump to any file. Press Ctrl-T, type fragments of the filename, Enter. No more tab-completing through deep trees.

2. Re-run long commands. Press Ctrl-R, type two or three fragments in any order, Enter. A deploy command from last Tuesday returns in seconds.

3. Change directories fast. Press Alt-C, type part of the target name, Enter. Deep project trees collapse to a few keystrokes.

4. Switch git branches. This alias lists branches in fzf and checks out the pick:

alias gco="git branch --all | grep -v HEAD | fzf | tr -d ' *' | xargs git checkout"

5. Kill a process without reading PIDs. Pick from the process list visually instead of copying numbers:

alias fkill="ps aux | fzf | awk '{print \$2}' | xargs kill"

6. Search then edit. The rgi function plus the editor snippet from the previous section. Find text, preview context, land in the editor on the exact line. When I debug unfamiliar codebases, this loop runs dozens of times a day, and the same search-first habit helps when you debug AI coding agents that get stuck in large generated files.

One more integration worth setting up: if you write custom instructions for coding assistants, a fast local search makes verifying their output cheap. I keep my assistant rules tight, as described in my rules files guide, and fzf plus ripgrep is how I confirm the agent touched the files it claims.

Frequently Asked Questions

Should ripgrep replace grep completely? On machines you control, yes for interactive use. Keep grep knowledge for fresh servers and minimal containers where installing packages is not an option. Scripts meant to run anywhere should still call grep.

Is fzf a replacement for file managers? No. It replaces the finding step, not the browsing step. For exploring an unknown directory tree visually, a tree view or file manager still wins. fzf wins when you know roughly what you want and need it now.

Does fuzzy matching slow down large lists? fzf handles hundreds of thousands of entries smoothly because matching runs incrementally as you type. For content search across repos, the reload pattern above keeps the list small anyway since ripgrep pre-filters each keystroke.

What about fd as a find replacement? fd is the natural companion: fast file-name search with sane defaults, and it plugs into fzf as the default source for Ctrl-T. If you install the pair from this guide, add fd next. Set FZF_DEFAULT_COMMAND='fd --type f' and your Ctrl-T picker ignores gitignored files automatically.

Final Take

Install ripgrep for speed and fzf for interaction. Learn five ripgrep flags, three fzf shortcuts, and the reload pattern that joins them. That small set removes the slowest part of terminal work and compounds every day you code. If you spend your days in a terminal alongside newer AI tooling, my OpenCode CLI overview pairs well with this setup. Set aside twenty minutes today, wire up the config, and your next search will feel instant.

Irfan is a Creative Tech Strategist and the founder of Grafisify. He spends his days testing the latest AI design tools and breaking down complex tech into actionable guides for creators. When he’s not writing, he’s experimenting with generative art or optimizing digital workflows.

Leave a Reply

Your email address will not be published. Required fields are marked *

You might also like
Passkeys vs Passwords: Which Is More Secure?

Passkeys vs Passwords: Which Is More Secure?

SPF, DKIM, and DMARC: A Practical Setup Guide

SPF, DKIM, and DMARC: A Practical Setup Guide

How to Build a Home Server with a Raspberry Pi

How to Build a Home Server with a Raspberry Pi

Nginx Reverse Proxy for Docker: Practical Setup Guide

Nginx Reverse Proxy for Docker: Practical Setup Guide

How to Speed Up a Slow WordPress Site

How to Speed Up a Slow WordPress Site

GitHub Actions Cache: Cut CI Time and Cost

GitHub Actions Cache: Cut CI Time and Cost