Linux 'grep'
Preview
grep is the fastest way to filter lines that match a pattern across files, directories, or streams. This guide focuses on GNU grep with production-ready examples.
TL;DR (Cheatsheet)
- Basic:
grep PATTERN file - Case-insensitive:
-i - Line numbers:
-n - Count only:
-c - Filenames only:
-l(inverse-L) - Matched text only:
-o - Extended regex:
-E - Fixed string (no regex, faster):
-F - Invert match:
-v - Recursive:
-r(no symlinks),-R(follow symlinks) - Context:
-B N,-A N,-C N - Include/exclude globs:
--include,--exclude,--exclude-dir - Color:
--color=auto(force with--color=always) - Skip binaries:
-I - NUL-safe piping:
-Z(use withxargs -0)
Core Patterns You’ll Actually Use
# Recursive search with file filters
grep -Rni --include='*.log' --exclude-dir='.git' 'ERROR' .
# Count matches per file (quick signal for CI)
grep -Rc --include='*.py' 'TODO' src/
# Show only the matched portion (e.g., HTTP status in journal)
journalctl -u nginx | grep -oE 'status=[0-9]{3}'
# Word boundary search (avoid over-matching)
grep -rw --include='*.c' '\<init\>' src/
# Literal text (fast, ignores regex metacharacters)
grep -F '[INFO]' app.log
# Safe with odd filenames (spaces/newlines)
find . -name '*.md' -print0 | xargs -0 grep -n 'architecture'
Performance That Matters
- Prefer
-Ffor literal patterns (often much faster). -
Use
LC_ALL=Cfor byte-wise matching and speed on huge inputs:LC_ALL=C grep -rF 'needle' /data/terabytes - Skip binaries with
-Ior--binary-files=without-match. - Stop early with
-m Nwhen you only need the first N hits. - In pipelines where latency matters, some tools support
--line-buffered; GNUgrepauto-flushes on line end, but overall pipeline buffering still applies.
Regex Primer (Just Enough)
-
-E(ERE):foo|bar,a{2,5},colou?r,(ba|be)z. -
-P(PCRE) for lookarounds:grep -Po '(?<=user=)[^ &]+' access.logNote:
-Pmay be missing on stock BSD/macOS grep. Use GNU grep (e.g., Homebrewggrep) or rewrite withawk/sed.
Context & Reporting
# Show surrounding lines (2 before/after)
grep -nC2 'panic' kernel.log
# Only filenames that contain matches
grep -Rl 'TODO' src/
# Suppress filenames (multi-file, lines only)
grep -h 'pattern' file1 file2
Real-World Mini Cookbook
# 1) Summarize error bursts with context around each match
grep -Rni --include='*.log' 'CRITICAL' logs/ | sed -n 's/:.*//p' | sort -u \
| xargs -I{} sh -c "echo '--- {} ---'; grep -nC2 'CRITICAL' {}"
# 2) Extract JSON values (simple cases)
grep -Po '"user_id"\s*:\s*"\K[^"]+' events.jsonl | sort | uniq -c | sort -nr | head
# 3) Find lines NOT matching (e.g., exclude health checks)
grep -R 'GET /' access.log | grep -v '/healthz'
Cross-Platform Notes
-
GNU vs BSD (macOS) differences:
-
-Poften unavailable on BSD grep. - Some long options vary; always
man grep. - Install GNU grep if you need PCRE or consistent behavior.
-
Common Pitfalls (and Fixes)
- Regex when you wanted literal → use
-F. - Locale surprises / poor performance on huge inputs →
LC_ALL=C. - Recursive slowness → narrow with
--include/--excludeand prefer-F. - Useless use of
cat→grep pattern file(pipe only when needed).