Skip to content

ripgrep (rg) — Fast Search Primer

Why This Matters

ripgrep is grep rebuilt for modern codebases. It is faster than grep, ag, and ack in nearly every benchmark. It respects .gitignore by default, supports PCRE2 regex, and handles Unicode correctly. For DevOps engineers searching through logs, configs, and code across large repos, rg eliminates the wait.

Name origin: ripgrep (rg) was created by Andrew Galloway in 2016, written in Rust. The "rip" stands for both speed ("ripping through files") and the Rust implementation. In benchmarks, rg is typically 2-10x faster than grep on large directory searches because it uses memory-mapped I/O, parallelism, and avoids searching inside .git directories.

Fun fact: ripgrep powers the search in VS Code, Sublime Text, and many other editors. When you use Ctrl+Shift+F in VS Code, it is ripgrep doing the work.

Core Concepts

# Search for a pattern in the current directory (recursive by default)
rg "connection refused"

# Search a specific file
rg "timeout" /etc/nginx/nginx.conf

# Search a specific directory
rg "ERROR" /var/log/

rg is recursive by default — no -r flag needed. It automatically skips binary files and respects .gitignore.

Gotcha: rg exits with code 1 when no matches are found — this trips up shell scripts using set -e. Use rg "pattern" || true or check $? explicitly if "no matches" is an expected outcome.

Case Sensitivity

# Case-insensitive
rg -i "error"

# Smart case (case-insensitive unless pattern has uppercase)
rg -S "Error"

Output Control

Line Numbers and Context

# Show line numbers (default when outputting to terminal)
rg -n "segfault" /var/log/syslog

# Lines before match
rg -B 3 "panic" app.log

# Lines after match
rg -A 5 "FATAL" app.log

# Lines before and after
rg -C 2 "OOMKilled" events.log

Count and Files Only

# Count matches per file
rg -c "TODO" src/

# List files with matches (no content)
rg -l "password"

# List files WITHOUT matches
rg --files-without-match "license" .

Type Filters

rg has built-in file-type awareness:

# Search only Python files
rg -t py "import requests"

# Search only YAML files
rg -t yaml "replicas:"

# Exclude a type
rg -T js "config"

# List known types
rg --type-list

Common types: py, go, js, ts, yaml, json, md, sh, tf, docker.

Glob Patterns

Fine-grained file filtering:

# Only search .conf files
rg -g '*.conf' "listen"

# Exclude test directories
rg -g '!tests/' -g '!*_test.go' "func "

# Only Helm templates
rg -g 'templates/*.yaml' "{{ .Values"

Regular Expressions

rg uses Rust regex by default (fast). Add -P for PCRE2 (lookaheads, backreferences):

# Standard regex
rg "error|warn|fatal" /var/log/syslog

# Word boundary
rg -w "port"    # matches "port" but not "export"

# PCRE2 with capture groups
rg -P "timeout=(\d+)" --only-matching

Replace

rg can preview replacements (does not modify files):

rg "oldfunction" --replace "newfunction"

# Pipe to sed for actual file modification
rg -l "oldvalue" | xargs sed -i 's/oldvalue/newvalue/g'

Hidden Files and Ignores

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

# Include files ignored by .gitignore
rg --no-ignore "TODO"

# Include everything (hidden + ignored + binary)
rg -uuu "pattern"

The -u flag stacks: | Flag | Effect | |------|--------| | -u | Include .gitignore'd files | | -uu | Also include hidden files | | -uuu | Also include binary files |

Configuration File

Set RIPGREP_CONFIG_PATH to a file with one flag per line (e.g., --smart-case, --glob=!.git/). These apply to every rg invocation.

One-liner: export RIPGREP_CONFIG_PATH="$HOME/.ripgreprc" then add --smart-case and --max-columns=200 to the file. Smart case (case-insensitive unless your pattern has uppercase) is the single most useful default.

rg vs grep

rg is multi-threaded, .gitignore-aware, Unicode-complete, recursive by default, and skips binary files. Typical speedup is 2-10x on large repos.

Remember: When to still use grep: inside containers (rg is not installed), parsing single files where speed does not matter, and when you need POSIX portability across AIX/Solaris/FreeBSD. For everything else, rg wins. Mnemonic: "rg for repos, grep for pipes" — use rg for directory searches, grep for filtering piped output.

Under the hood: rg's speed comes from three design decisions: (1) it uses memory-mapped I/O to avoid read syscall overhead, (2) it parallelizes directory traversal across CPU cores, and (3) it uses Rust's regex crate which compiles patterns to automata — avoiding the exponential backtracking that slows grep on pathological patterns.

Practical DevOps Patterns

# Find all hardcoded IPs
rg -P '\b\d{1,3}(\.\d{1,3}){3}\b' --type yaml

# Find secrets that should not be in code
rg -i "(password|secret|api.?key)\s*[:=]" --type-not md

# Search compressed logs
zcat /var/log/syslog.1.gz | rg "error"

# rg + fzf: interactive search results
rg --color=always "TODO" | fzf --ansi

Wiki Navigation