ripgrep searches file contents like grep, but it is faster, it ignores .git and anything in your .gitignore without being asked, and it recurses by default. On a large codebase the difference is not subtle — searches that took ten seconds take under one.

That does not make grep obsolete, and the reason why is worth understanding before you rewrite your habits.

The short verdict

Install ripgrep and use it interactively. Searching a project, hunting through source, grepping a directory tree — rg wins on every axis that matters when a human is waiting.

Keep grep for scripts, pipes and other people’s machines. It is on every Unix system ever made, its behaviour is specified by POSIX, and it will still be there in twenty years.

Both, in other words. They are not really competing for the same job.

What ripgrep changes

grepripgrep
Recurses by defaultNo, needs -rYes
Respects .gitignoreNoYes, automatically
Skips .git, binaries, hidden filesNoYes
Searches in parallelNoYes, across cores
Colour and line numbersOptional flagsOn by default
Filter by file typeManual --include globs-t py, -t rust
Regex enginePOSIX BRE/ERE, PCRE with -PRust regex, PCRE2 with -P
Multiline matchingAwkward-U
Installed everywhereYesNo
POSIX specifiedYesNo

Most of ripgrep’s speed advantage is not clever engineering, though there is plenty of that. It is that ripgrep does not search files you did not want searched. No node_modules, no .git objects, no compiled binaries, no minified bundles. On a typical project that removes most of the bytes before any searching happens.

Which is also the one behaviour that will surprise you, so it is worth knowing before it does: if a file is gitignored, rg will not find matches in it unless you ask.

# Include ignored files
rg --no-ignore "pattern"

# Include hidden files (dotfiles)
rg --hidden "pattern"

# Search absolutely everything, including binaries
rg -uuu "pattern"

-uuu is the escape hatch: each -u removes one layer of filtering, and three of them makes rg behave roughly like grep -r. If a search comes back empty and you are sure the text is there, try that before doubting yourself.

Installing it

# Debian, Ubuntu
sudo apt install ripgrep

# Fedora, RHEL, Rocky, Alma
sudo dnf install ripgrep

# Arch
sudo pacman -S ripgrep

# macOS
brew install ripgrep

The package is called ripgrep; the command is rg. That trips people up on the first install.

Translating what you already know

The good news is that the flags you use most are identical. -i, -v, -c, -l, -w, -A, -B and -C all mean the same thing in both.

You wantgrepripgrep
Search a treegrep -rn "x" .rg "x"
Case insensitivegrep -irg -i
Whole wordgrep -wrg -w
Invert the matchgrep -vrg -v
Filenames onlygrep -rlrg -l
Count matchesgrep -crg -c
Context linesgrep -C 3rg -C 3
Fixed string, no regexgrep -Frg -F
Only Python filesgrep -r --include="*.py"rg -t py
Exclude Python filesgrep -r --exclude="*.py"rg -T py
Glob filter--include="*.conf"rg -g "*.conf"
Show only the matchgrep -org -o
Search a single filegrep "x" filerg "x" file

The file type filter is the feature worth learning deliberately, because it has no convenient grep equivalent:

# Only in Python files
rg -t py "def handle"

# Everything except tests
rg -T test "deprecated"

# What types does it know about?
rg --type-list | less

Where the regex differs

ripgrep’s default syntax is closer to what most people expect than grep’s basic regular expressions, where +, ?, | and grouping all need backslashes. rg behaves like grep -E out of the box.

# grep, basic regex - note the backslashes
grep "error\|warning" file

# grep extended
grep -E "error|warning" file

# ripgrep - extended by default
rg "error|warning" file

The one real gap: ripgrep’s Rust regex engine deliberately has no backreferences and no lookaround, because it guarantees linear-time matching and those features make that impossible. If you need (?<=foo) or \1, add -P to use PCRE2 instead.

# Lookbehind requires PCRE2
rg -P "(?<=version: )[0-9.]+" config.yml

Where grep is still the right answer

This is the part most “modern tools” write-ups skip, and it matters more than the benchmark does.

  • Scripts you will run on other machines. grep is guaranteed present; rg is not. A deployment script that assumes ripgrep breaks on a minimal container or a server you do not control.
  • Reading from a pipe. rg works in a pipe, but this is grep’s home ground and there is no advantage to switching: journalctl -u nginx | grep error is idiomatic and everyone reading it knows exactly what it does.
  • Someone else’s server at 3am. You will not be installing packages on a machine mid-incident. The tool you can rely on is the one already there.
  • Portable shell scripts. POSIX-specified behaviour means the same result on Linux, BSD, macOS and AIX. Nothing about ripgrep is standardised.
  • Searching files git ignores, unless you remember the flags — log files, build output and node_modules are exactly the things ripgrep hides.

The general rule, and it applies to every pair in this section: learn the classic tool, install the modern one. Knowing grep is a portable skill that works on any Unix machine you are ever dropped onto. Having ripgrep on your own machines makes your daily work faster. These are not in conflict.

Things worth configuring

ripgrep reads a config file, but only if you tell it where one is:

# In ~/.bashrc or ~/.zshrc
export RIPGREP_CONFIG_PATH="$HOME/.config/ripgrep/config"
# ~/.config/ripgrep/config - one flag per line, no quotes needed
--smart-case
--hidden
--glob=!.git/*
--max-columns=200
--max-columns-preview

--smart-case is the best default in that list: searches are case insensitive if your pattern is all lowercase, and case sensitive the moment you type a capital. It is what you meant nearly every time.

One caution: a config file changes behaviour invisibly, which is a problem if you paste a command into a script or hand it to a colleague. Use rg --no-config when you want to be certain what a command does.

Quick reference

You wantCommand
Search everything below hererg "pattern"
Case insensitiverg -i "pattern"
Only one file typerg -t py "pattern"
Filenames onlyrg -l "pattern"
With three lines of contextrg -C 3 "pattern"
Include ignored and hidden filesrg -uu "pattern"
Absolutely everythingrg -uuu "pattern"
Lookaround or backreferencesrg -P "pattern"
Across multiple linesrg -U "pattern"
Which file types existrg --type-list

Related reading