Ripgrep: 10 Commands That Make grep Feel Ancient

If you still use grep for anything beyond a quick search in one file, you are wasting time.

I switched to ripgrep (rg) about two years ago and never looked back. It respects your .gitignore by default. It searches faster than anything else I have used. And it plays nicely with the rest of the modern terminal stack: fzf, bat, fd.

Ripgrep is written in Rust. It recursively searches directories for a regex pattern while respecting your gitignore rules. No flags needed. Just point it at a directory and watch it fly.

Here are the commands that matter.

1. The basics: rg vs grep

The simplest use case. Search for “error” across your whole project:

rg "error"

That is it. No -r flag. No –recursive. Ripgrep defaults to recursive search. Compare to grep where you type grep -r "error" . every single time.

Ripgrep also highlights matches in color by default. Grep needs –color=auto. Small wins add up.

2. Search specific file types

You don’t want to search binary files or node_modules. Ripgrep knows that. But sometimes you want to narrow further.

rg -t py "def main"     # Python only
rg -t js "useEffect"     # JavaScript/TypeScript only
rg -t md "TODO"          # Markdown files only

The -t flag uses ripgrep’s built-in type definitions. Run rg --type-list to see all supported types. There are dozens: py, js, rs, go, java, cpp, md, html, css, yaml, toml, and more.

Want to exclude a type? Use -T instead of -t.

rg -T js "class"    # Search everything except JS files

3. Case control

Case-insensitive search is common enough that ripgrep has a short flag for it.

rg -i "getUser"      # Case insensitive
rg -S "getUser"      # Smart case: insensitive if pattern is all lowercase, sensitive if mixed case

Smart case (-S) is my default. If I type rg -S "getUser", it knows I mean capital U. If I type rg -S "user", it ignores case. One less thing to think about.

4. Show context around matches

Sometimes you need to see what surrounds a match, not just the matching line.

rg -C 3 "TODO"       # 3 lines before and after
rg -B 2 "error"      # 2 lines before only
rg -A 5 "function"   # 5 lines after only

I use -C 2 constantly when debugging. It shows me the function signature above and the line below without opening the file.

5. Count matches per file

Need to know which file has the most occurrences of something?

rg -c "import" src/
# Output: src/main.py: 12
#         src/utils.py: 8
#         src/config.py: 3

Add -c with –sort to get ranked results. Useful for finding hotspots in your codebase.

6. Print only matching files (not lines)

This is the one I use most for refactoring. Find all files that contain a pattern, without seeing the matches.

rg -l "oldFunctionName" src/
# Output: src/main.py
#         src/helpers.py

Pipe that to xargs sed for bulk replacements. Or feed it to fzf for interactive selection.

rg -l "deprecated_api" --null | xargs -0 sed -i 's/deprecated_api/new_api/g'

The –null flag uses null separators so filenames with spaces won’t break your pipeline.

7. Invert match (find files without something)

Find files that do NOT contain a pattern. Great for checking if you missed something.

rg -l --invert-match "copyright" src/
# Files that are missing a copyright header

I use this before committing to find files that forgot the license header.

8. Multi-line searches

Grep cannot search across multiple lines without serious gymnastics. Ripgrep handles it with -U (multiline mode).

rg -U "if \(.*\)\n\s+\{" src/

This finds an if statement followed by an opening brace on the next line. The regex spans newlines. It is slower than single-line search, but still faster than any alternative I have tried.

9. Replace in place

Ripgrep 13+ has a –replace flag that previews replacements. Pair it with –passthru to see the full file with changes highlighted.

rg --replace "new_api" "old_api" src/main.py
# Preview only. Does not write to the file.

For actual in-place replacement, use the –passthru flag and redirect, or combine with sed as shown above. I preview first, then apply. Saves me from regex disasters.

10. Integration with fzf and bat

This is where ripgrep really shines. Feed its output into fzf for an interactive search interface.

rg -l "TODO" | fzf --preview "bat --color=always {}"

This shows a list of files containing TODO. Select one with arrow keys and bat previews the content on the right. It works like VS Code’s search panel, but in your terminal and for any project.

I mapped this to Ctrl-F in my shell config. One keystroke, instant project-wide search with previews.

Bonus: Speed comparison

I tested this on a 50K-file codebase searching for a common word. Here is what I saw:

  • grep -r: 8.4 seconds
  • ag (the silver searcher): 2.1 seconds
  • ripgrep: 0.6 seconds

Ripgrep is not marginally faster. It is an order of magnitude faster than grep and 3x faster than ag. The difference is noticeable the first time you use it.

Getting started

Install ripgrep on any system:

# Debian/Ubuntu
sudo apt install ripgrep

# macOS
brew install ripgrep

# Arch
pacman -S ripgrep

# Fedora
dnf install ripgrep

# Or download a static binary from GitHub releases
https://github.com/BurntSushi/ripgrep/releases

That is the whole setup. No config file needed. No environment variables. It just works.

One more thing

Alias rg to something shorter if you want, but rg is already three characters. I added alias r='rg' in my .zshrc and never looked at grep again. Well, almost never. Old habits.

For the official ripgrep docs, check the GitHub page: https://github.com/BurntSushi/ripgrep. The man page is also excellent: man rg.

Leave a Reply

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