grep — Gotchas
Syntax Traps
BRE vs ERE vs PCRE — the + trap
# WRONG — BRE treats + as literal
grep 'go+d' file # matches "go+d" literally
# CORRECT — ERE interprets + as quantifier
grep -E 'go+d' file # matches "god", "good", "goood"
# ALSO CORRECT — PCRE
grep -P 'go+d' file
Pipe in grep pattern vs shell pipe
# WRONG — shell interprets the pipe
grep error|warning file
# CORRECT — quote the pattern
grep -E 'error|warning' file
Empty pattern matches everything
# WRONG — empty string matches every line
grep '' file
# CORRECT — be explicit
grep -c '^$' file # count blank lines
Portability and Edge Cases
grep -r follows symlinks by default
# Avoid infinite loops in repos with circular symlinks
grep -rP --no-messages 'pattern' .
# Or use find for precise control
find . -type f -name '*.log' -exec grep -lP 'pattern' {} +
Backtick pipes in AsciiDoc tables
# AsciiDoc table parser runs BEFORE inline formatting
# | `cmd1 | cmd2` → parsed as THREE cells, not one
# | `cmd1 \| cmd2` → parsed as ONE cell containing a pipe
Verify your own fix doesn’t show as false positive
# After escaping {search_id} → \{search_id}, naive verify still matches:
grep -rnP '{search_id}' --include='*.adoc' docs/
# Shows ALL instances — including the ones you just fixed!
# CORRECT — negative lookbehind excludes escaped instances
grep -rnP '(?<!\\)\{search_id\}' --include='*.adoc' docs/
# Returns nothing = all fixed