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
| grep | ripgrep | |
|---|---|---|
| Recurses by default | No, needs -r | Yes |
Respects .gitignore | No | Yes, automatically |
Skips .git, binaries, hidden files | No | Yes |
| Searches in parallel | No | Yes, across cores |
| Colour and line numbers | Optional flags | On by default |
| Filter by file type | Manual --include globs | -t py, -t rust |
| Regex engine | POSIX BRE/ERE, PCRE with -P | Rust regex, PCRE2 with -P |
| Multiline matching | Awkward | -U |
| Installed everywhere | Yes | No |
| POSIX specified | Yes | No |
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 ripgrepThe 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 want | grep | ripgrep |
|---|---|---|
| Search a tree | grep -rn "x" . | rg "x" |
| Case insensitive | grep -i | rg -i |
| Whole word | grep -w | rg -w |
| Invert the match | grep -v | rg -v |
| Filenames only | grep -rl | rg -l |
| Count matches | grep -c | rg -c |
| Context lines | grep -C 3 | rg -C 3 |
| Fixed string, no regex | grep -F | rg -F |
| Only Python files | grep -r --include="*.py" | rg -t py |
| Exclude Python files | grep -r --exclude="*.py" | rg -T py |
| Glob filter | --include="*.conf" | rg -g "*.conf" |
| Show only the match | grep -o | rg -o |
| Search a single file | grep "x" file | rg "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 | lessWhere 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" fileThe 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.ymlWhere 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.
grepis guaranteed present;rgis not. A deployment script that assumes ripgrep breaks on a minimal container or a server you do not control. - Reading from a pipe.
rgworks in a pipe, but this is grep’s home ground and there is no advantage to switching:journalctl -u nginx | grep erroris 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_modulesare 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 want | Command |
|---|---|
| Search everything below here | rg "pattern" |
| Case insensitive | rg -i "pattern" |
| Only one file type | rg -t py "pattern" |
| Filenames only | rg -l "pattern" |
| With three lines of context | rg -C 3 "pattern" |
| Include ignored and hidden files | rg -uu "pattern" |
| Absolutely everything | rg -uuu "pattern" |
| Lookaround or backreferences | rg -P "pattern" |
| Across multiple lines | rg -U "pattern" |
| Which file types exist | rg --type-list |
Related reading
- grep — the classic, in full
- fd vs find — the same trade-off, for filenames instead of contents
- htop and btop vs top — watching processes
- awk — when you need to do something with what you found
- Reading Linux logs — where grep in a pipe is still the idiom
- Package management — installing ripgrep on your distribution
