Everyday Search
rg "pattern"
Recursive search from the current directory. Skips .gitignore'd files, hidden files and binaries — which is why it sometimes "misses" things.
Example: rg "TODO" - quote the pattern whenever it contains spaces or shell characters
rg -S "pattern"
Smart case: insensitive until your pattern contains a capital letter. The best everyday default — alias it and stop thinking about -i.
Example: alias rg='rg -S' - then rg error matches all casings, rg Error only matches Error
rg -i "pattern"
Force case-insensitive, even when the pattern has capitals. Use when you know the casing in the codebase is inconsistent.
Example: rg -i "connection refused" - matches any capitalisation
rg -w "word"
Whole words only. The fastest single way to cut noise when your term is a common substring.
Example: rg -w "id" - matches id, but not uuid, ident or valid
rg "pattern" src/ tests/
Search one or more specific paths. Narrowing up front beats filtering the results afterwards.
Example: rg "useAuth" src/hooks src/pages - several paths in one command
rg -F "app.config[0]"
Literal string, regex disabled. Reach for this any time the term has dots, brackets, parens or slashes in it.
Example: rg -F "$_POST['id']" - no escaping needed anywhere
rg -l "pattern"
Filenames only, one per line. This is the form you pipe into other commands.
Example: rg -l "deprecated" - 12 filenames instead of 400 matching lines
rg --files-without-match "pattern"
The inverse of -l: files that do not contain the pattern. Good for auditing what is missing rather than what is there.
Example: rg --files-without-match "@license" -g "*.js" - JS files with no licence header
Scoping Files & Directories
rg -t py "pattern"
Restrict to a file type. Faster than an equivalent glob, because rg rejects files before opening them.
Example: rg -t py "def process" - covers .py, .pyi and other Python extensions
rg -t js -t ts "pattern"
Repeat -t to include several types. Types are additive, not exclusive.
Example: rg -t js -t ts "useEffect" - the whole JS/TS surface in one pass
rg -T md "pattern"
Exclude a type with -T. Use it to keep docs, lockfiles or fixtures out of code searches.
Example: rg -T md "install" - skip every Markdown file, search only code
rg -g "*.{ts,tsx}" "pattern"
Glob when no built-in type fits, or when you need one precise set of extensions.
Example: rg -g "*.{yml,yaml}" "image:" - both YAML spellings
rg -g '!node_modules' "pattern"
A leading ! negates the glob. This is how you exclude a directory, and it is the flag missing from most cheatsheets.
Example: rg -g '!{node_modules,dist,build,*.min.js}' "apiKey" - exclude several at once
rg -g "**/migrations/**" "pattern"
Restrict to a directory pattern at any depth in the tree.
Example: rg -g "**/test/**" "fixture" - only inside test directories
rg -uu "pattern"
One -u ignores .gitignore. Two also searches hidden files. Three also searches binaries. Add u's until the file turns up.
Example: rg -uu "TODO" - includes .env, .github and everything git is hiding
rg --hidden "pattern"
Include dotfiles and dot-directories while still honouring .gitignore. Short form is -. (a bare dot).
Example: rg --hidden "AWS_" -g '!.git' - search dotfiles but skip .git internals
rg --no-ignore-vcs "pattern"
Bypass .gitignore but keep obeying .ignore and .rgignore. Lets you see build output without losing your own excludes.
Example: rg --no-ignore-vcs "sourceMappingURL" - finds generated files
rg --max-depth 1 "pattern"
Cap the recursion depth. A depth of 1 means the current directory only, no subdirectories.
Example: rg --max-depth 2 "version" . - top two levels only
rg --files -t py
List the files rg would search, with no pattern at all. Filter it with -t or -g rather than piping into grep.
Example: rg --files -g '!node_modules' - every file in your real project
Regex & Patterns
rg "^import"
^ anchors to the start of the line. Cheap, precise, and cuts out indented or commented occurrences.
Example: rg "^from \w+ import" - top-level Python imports only
rg "\s+$"
$ anchors to the end of the line.
Example: rg "\s+$" -t py - every line with trailing whitespace
rg "(GET|POST|PUT|DELETE) /api"
Alternation and grouping. Inside quotes you never need to escape the parens or the pipe.
Example: rg "(warn|warning|WARN)\b" - several spellings in one pass
rg -e "pattern1" -e "pattern2"
Multiple independent patterns. Also the only way to search for a term that begins with a dash, which rg would otherwise read as a flag.
Example: rg -e "--force" -e "-f " - dash-leading patterns
rg -v "pattern"
Invert: print the lines that do not match. Combine with a second rg to whittle noise down in stages.
Example: rg -v "^\s*(#|$)" config.ini - drop comments and blank lines
rg -P "(?<=version: )\d+\.\d+\.\d+"
-P switches to the PCRE2 engine. Lookahead and lookbehind do not exist in rg's default engine, so without -P these patterns error out.
Example: rg -P "password(?!_hash)" - password but never password_hash
rg -P "\b(\w+) \1\b"
Backreferences also need PCRE2. This one finds accidentally doubled words.
Example: rg -P "\b(\w+) \1\b" -t md - "the the" typos across your docs
rg -U --multiline-dotall "class Foo.*?\}"
-U lets a match span lines, but . still stops at a newline until you add --multiline-dotall. Missing that second flag is why most multiline examples silently fail. Keep quantifiers lazy (.*?) or you will match the whole file.
Example: rg -U "try \{[\s\S]*?catch" - same result using [\s\S] instead of the flag
rg -f patterns.txt
Read patterns from a file, one per line. Pass - to take the list from stdin instead.
Example: rg -f indicators.txt /var/log/ - hunt a whole list across your logs
Reading Output
rg -C 3 "pattern"
Three lines of context on both sides. The single most useful flag for actually understanding a hit.
Example: rg -C 5 "raise ValueError" - see the surrounding logic
rg -F -A 20 "Traceback (most recent call last)"
-A is lines after, -B is lines before. Asymmetric context is what you want for stack traces and multi-line log entries.
Example: rg -B 2 -A 20 "Traceback" - the error plus its whole trace
rg -p "pattern" | less -R
rg drops colour and grouping the moment output is piped. -p forces the terminal formatting back on so less can render it.
Example: rg -p "TODO" | less -R - paged, coloured, grouped by file
rg -M 200 --max-columns-preview "pattern"
Cap how much of a long line prints, so one minified file cannot flood the terminal. --max-columns-preview shows a truncated preview instead of suppressing the line entirely.
Example: rg -M 150 --max-columns-preview "apiKey" -g "*.min.js" - readable output from minified code
rg --sortr modified "pattern"
Newest files first — the only sensible order for logs. --sort does oldest first. Both force single-threaded searching, so use them on narrow scopes.
Example: rg --sortr modified -l "OOMKilled" /var/log/ - most recent occurrence at the top
rg --no-heading -n "pattern"
Flat file:line:text on every row instead of results grouped under filename headings. Easier to scan, and each line stands alone when pasted.
Example: rg --no-heading -n "func " -t go - one self-contained line per match
rg -IN "pattern"
-I drops filenames, -N drops line numbers. What is left is clean text you can copy straight into something else.
Example: rg -IN -o "https?://\S+" -t md - a bare list of URLs
rg --trim "pattern"
Strip leading whitespace from each result line, so deeply indented code stops wrapping in your terminal.
Example: rg --trim -C 2 "return null" -t java
rg --column "pattern"
Add column numbers, implying line numbers too. Needed by any tool that jumps to an exact position.
Example: rg --column "TODO" - prints file:line:col:text
Extract & Count
rg -c "pattern"
Counts matching LINES per file, not matches. A line with three hits still counts as one — this is the surprise behind most wrong totals.
Example: rg -c "import" -t py - per-file line counts
rg --count-matches "pattern"
Counts every match, including several on the same line. This is the number you actually meant when -c disappointed you.
Example: rg --count-matches "\bTODO\b" - the true total
rg -c --include-zero "pattern"
List files with zero matches alongside the rest, so the report covers everything searched.
Example: rg -c --include-zero "assert" -t py - files with no assertions show as 0
rg -o "pattern"
Print only the matched text rather than the whole line. Every extraction pipeline starts here.
Example: rg -o "https?://[^\s\"')]+" -t md - just the URLs, not their sentences
rg -o -r '$1' "version=\"([^\"]+)\""
-r rewrites output using capture groups; with -o you get the captured value on its own. Single-quote the replacement or your shell will eat $1 before rg sees it.
Example: rg -o -r '$1' '"version": "([^"]+)"' package.json - the version, nothing else
rg -IN -o "pattern" | sort | uniq -c | sort -rn
The frequency tally: extract, group, rank. This answers "which of these appears most", which no single rg flag does.
Example: rg -IN -o '"[a-z_]+":' -t json | sort | uniq -c | sort -rn - most common JSON keys
rg -r 'new_$1' "old_(\w+)"
Preview a rewrite on stdout. rg never edits files — there is no in-place mode — so this is purely a dry run before you hand the job to sed.
Example: rg -r 'logger.info($1)' 'print\((.+)\)' -t py - inspect the refactor first
rg --json "pattern" | jq
JSON Lines output with byte offsets and submatch positions. Use this in scripts instead of parsing rg's human-readable text.
Example: rg --json "TODO" | jq -r 'select(.type=="match") | .data.path.text'
rg --stats "pattern"
Append a summary: matches, matched lines, files with matches, files searched, elapsed time.
Example: rg --stats "TODO" -t py - handy for tracking cleanup progress week to week
Pipelines & Refactoring
rg -l0 "old" | xargs -0 sed -i 's/old/new/g'
Repo-wide find and replace, the safe way. rg locates the files, sed makes the edit. -l0 and xargs -0 use NUL separators so paths with spaces do not break.
Example: on macOS, sed needs an empty argument: sed -i '' 's/old/new/g'
rg -l "old" | xargs sed -i 's/old/new/g'
The shorter form of the same thing. Fine for tidy paths, but it will mangle filenames containing spaces — prefer the -l0 variant.
Example: rg -l "getUserById" | xargs sed -i 's/getUserById/findUser/g'
rg -l "pattern" | wc -l
How many files a change would touch. The fastest way to size a refactor before starting it.
Example: rg -l "deprecated_api" | wc -l - 34 files to work through
rg -l "pattern" | xargs $EDITOR
Open every matching file at once and work through them in one sitting.
Example: rg -l "FIXME" -t ts | xargs nvim
rg -l0 "useState" | xargs -0 rg -l "useEffect"
Chain two searches to find files containing both patterns — an AND that a single regex cannot express across lines.
Example: rg -l0 "TODO" | xargs -0 rg -l "@deprecated" - files with both markers
rg --vimgrep "pattern"
file:line:col:text with exactly one row per match, which is the shape vim's quickfix list and most editor pickers expect.
Example: rg --vimgrep "TODO" > /tmp/q && vim -q /tmp/q
rg --vimgrep "pattern" | fzf
Pipe into fzf for interactive narrowing, with a preview pane to see each hit in place.
Example: rg --vimgrep "TODO" | fzf --delimiter=: --preview 'bat -n {1} --highlight-line {2}'
rg "pattern" $(git diff --name-only main)
Search only the files you have changed. A quick pre-commit check that never touches the rest of the repo.
Example: rg -n "console\.log" $(git diff --name-only --diff-filter=ACM main)
command | rg "pattern"
rg reads stdin, so it is a drop-in replacement for grep anywhere in a pipeline.
Example: curl -s https://api.example.com/health | rg -o '"status":"[^"]+"'
Logs & Big Files
rg -z "pattern" /var/log/
Search inside .gz, .bz2, .xz, .zst and .lz4 archives without unpacking them first.
Example: rg -z "OOMKilled" /var/log/pods/ - rotated logs included
tail -f app.log | rg --line-buffered -i "error"
--line-buffered is mandatory on a live stream. Without it rg buffers its output and you sit staring at nothing.
Example: kubectl logs -f deploy/api | rg --line-buffered -i "panic|fatal"
rg "ERROR" app.log | rg -v "noise"
Narrow in stages instead of building one enormous regex. Easier to write, and easier to adjust when it over-matches.
Example: rg "ERROR" app.log | rg -v "HealthCheck|favicon"
rg -a "pattern" dump.bin
-a treats binary files as text. By default rg stops at the first NUL byte and just warns you it found binary data.
Example: rg -a "BEGIN RSA PRIVATE KEY" core.dump - find keys in a memory dump
rg --max-filesize 5M "pattern"
Skip files above a size limit, so one stray database dump or 2 GB log cannot stall the whole search.
Example: rg --max-filesize 500K "TODO" -uu - stays fast even with filtering off
rg --crlf "pattern$"
Treat \r\n as the line ending. Without it, $ never matches on Windows-format files because \r sits between your pattern and the line end.
Example: rg --crlf "\d+$" export.csv - anchors work again
rg -p --passthru "pattern" app.log | less -R
Print every line but highlight the matches — reading the whole file with your search term lit up. -p keeps the colour alive through the pipe.
Example: rg -p --passthru "timeout" app.log | less -R
rg --pre pdftotext.sh -g "*.pdf" "pattern"
Run each file through a preprocessor before searching, which lets rg read PDFs, DOCX, or anything else it cannot parse natively. Your script takes the path as $1 and writes plain text to stdout.
Example: --pre scripts run per file, so keep them fast or scope them with -g
rg -b "pattern" archive.bin
Print the byte offset of each match, so you can seek straight to it with dd or tail -c.
Example: rg -ab "corrupt" archive.bin - offset to feed dd skip=
Fixing & Config
rg -uuu "pattern"
The first thing to try when rg finds nothing you know is there. Disables .gitignore, hidden-file skipping and binary detection in one go.
Example: rg -uuu "MY_SECRET" - searches .env, node_modules, build output and binaries
rg --files | rg "filename"
Check the file is even in the search set before blaming your pattern. No output means rg is excluding it, not failing to match.
Example: rg --files | rg "config.local" - silence here explains everything
rg --debug "pattern" 2>&1 | rg -i "ignor"
Shows exactly which ignore rule excluded a file and which file that rule came from. Ends the guessing.
Example: rg --debug "x" dist/ 2>&1 | rg -i "ignor"
rg --type-list
Every built-in type name and the globs it covers. Check here instead of guessing what -t accepts.
Example: rg --type-list | rg "tsx" - find which type owns .tsx files
rg --type-add 'web:*.{html,css,js,vue}' -t web "pattern"
Define your own type inline. Put the same string in your config file to keep it permanently.
Example: rg --type-add 'conf:*.{conf,cfg,ini,toml}' -t conf "port"
export RIPGREP_CONFIG_PATH=~/.config/ripgrep/rc
Point rg at a config file of default flags, one per line, # for comments. This is where --smart-case and your standing excludes belong.
Example: file contents, one per line: --smart-case then --max-columns=200 then --glob=!.git
RIPGREP_CONFIG_PATH= rg "pattern"
Unset the config for a single run. The way to find out whether one of your own defaults is causing the odd result.
Example: RIPGREP_CONFIG_PATH= rg -uuu "pattern" - a completely clean search
rg --no-require-git "pattern"
Apply .gitignore rules even outside a git repository, which rg skips by default. Useful in extracted tarballs and scratch directories.
Example: rg --no-require-git "TODO" ~/scratch/
rg --engine auto "pattern"
Use the fast default engine and fall back to PCRE2 automatically when the pattern needs it, so you stop having to remember -P.
Example: rg --engine auto "(?<=key=)\w+" - lookbehind just works
rg --help | rg -A 3 "max-filesize"
rg's own help is long and searchable with rg. Usually faster than opening the man page.
Example: rg --help | rg -A 3 "sort" - remind yourself of the sort options