rg — Behavioral Traps
Behavioral Traps
rg skips files matched by .gitignore, .rgignore, and .ignore by default. This means files in node_modules/, build/, or target/ are invisible.
WRONG — wondering why rg finds nothing in a gitignored directory:
rg 'pattern' build/
CORRECT — use --no-ignore to search everything:
rg --no-ignore 'pattern' build/
Layer precedence: .gitignore < .ignore < .rgignore. All three are respected by default.
rg detects binary files (by checking for NUL bytes) and skips them silently. grep processes them and produces garbled output.
WRONG — expecting to find matches in a binary file:
rg 'magic' /usr/bin/
CORRECT — explicitly opt in to binary search:
rg --binary 'magic' /usr/bin/
For text extraction from binaries, rg -a (alias for --text) treats all files as text. --binary is gentler — it searches binaries but replaces NUL bytes with placeholders.
Files and directories starting with . are invisible to rg unless told otherwise.
WRONG — searching for aliases but missing ~/.bashrc:
rg 'alias' ~/
CORRECT — include hidden files:
rg --hidden 'alias' ~/
Combine with --no-ignore for exhaustive search: rg --hidden --no-ignore 'pattern' dir/.
grep defaults to BRE (Basic Regular Expressions) where +, ?, {, ( are literal unless escaped. rg always uses an ERE-like syntax where these are metacharacters.
WRONG — escaping + as you would in grep BRE:
rg '\d\+\.\d\+' /etc/os-release
CORRECT — + is already a quantifier in rg:
rg '\d+\.\d+' /etc/os-release
This is a common trap for grep veterans. In rg, + matches a literal + character. In grep BRE, \+ means "one or more."
Lookaheads, lookbehinds, backreferences, \K — none of these work without -P.
WRONG — using a lookbehind without -P:
rg '(?<=port=)\d+' /etc/services
This produces an error: regex parse error.
CORRECT — enable PCRE2:
rg -P '(?<=port=)\d+' /etc/services | head -5
Check PCRE2 availability: rg --pcre2-version. If it says "unavailable," your build was compiled without it.
Glob patterns match against the path relative to the search directory, not the absolute path or cwd.
WRONG — assuming glob matches against the full path:
rg -g '*/ROOT/pages/*.adoc' 'pattern' ~/atelier/_bibliotheca/domus-captures/
CORRECT — glob is relative to the search root:
rg -g '*.adoc' 'pattern' ~/atelier/_bibliotheca/domus-captures/docs/modules/ROOT/pages/
For nested directory matching, use : -g '/pages//*.adoc'. The glob matches any number of directories.
rg exits with code 1 when no matches are found. In set -e scripts, this kills the script.
WRONG — unguarded rg in a strict script:
#!/bin/bash
set -e
count=$(rg -c 'pattern' file.txt) # exits 1 if no matches — script dies
CORRECT — guard with || true or test exit code:
#!/bin/bash
set -e
count=$(rg -c 'pattern' file.txt || true)
# OR
if rg -q 'pattern' file.txt; then
echo "found"
fi
Exit codes: 0 = matches found, 1 = no matches, 2 = error (bad regex, missing file).
rg searches files in parallel by default. Output order varies between runs.
WRONG — expecting stable output for diffs:
rg -l 'pattern' docs/ > /tmp/before.txt
# ... make changes ...
rg -l 'pattern' docs/ > /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt # false positives from reordering
CORRECT — force deterministic order:
rg --sort path -l 'pattern' docs/ > /tmp/before.txt
# ... make changes ...
rg --sort path -l 'pattern' docs/ > /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt
--sort path disables parallelism (necessarily). For large codebases, accept the speed cost or sort the output: rg -l 'pattern' | sort.