grep (Global Regular Expression Print) searches files for lines matching a pattern. It's one of the most-used Unix commands — for log analysis, code search, and pipeline filtering.
Quick reference
| Flag | What it does |
|---|---|
-i |
Case-insensitive match |
-r / -R |
Recursive search in directory |
-l |
Print only filenames with matches |
-L |
Print filenames with no matches |
-n |
Show line numbers |
-c |
Count matching lines per file |
-v |
Invert match (lines that don't match) |
-w |
Whole word match |
-x |
Whole line match |
-E |
Extended regex (same as egrep) |
-P |
Perl-compatible regex (PCRE) |
-F |
Fixed string, no regex (same as fgrep) |
-o |
Print only the matching part |
-A N |
N lines after match |
-B N |
N lines before match |
-C N |
N lines before and after (context) |
-m N |
Stop after N matches |
-q |
Quiet — exit 0 if match found, no output |
--include=GLOB |
Only search matching filenames |
--exclude=GLOB |
Skip matching filenames |
--exclude-dir=DIR |
Skip directory |
-H |
Always print filename (default for -r) |
-h |
Never print filename |
--color |
Highlight match in output |
-s |
Suppress error messages |
Basic usage
# Search for a pattern in a file
grep "error" /var/log/syslog
# Search in multiple files
grep "TODO" src/index.ts src/utils.ts
# Case-insensitive
grep -i "error" /var/log/syslog
# Show line numbers
grep -n "error" /var/log/syslog
# Count matches
grep -c "error" /var/log/syslog
# Invert: lines without the pattern
grep -v "DEBUG" app.log
Recursive directory search
# Search all files in directory
grep -r "TODO" ./src
# Also follow symlinks
grep -R "TODO" ./src
# Show only filenames (not content)
grep -rl "TODO" ./src
# Search specific file types
grep -r "useState" ./src --include="*.tsx"
grep -r "console.log" ./src --include="*.{js,ts}"
# Exclude files
grep -r "password" . --exclude="*.lock" --exclude-dir=node_modules
# Common: skip generated dirs
grep -r "apiUrl" . \
--include="*.ts" \
--exclude-dir=node_modules \
--exclude-dir=.git \
--exclude-dir=dist
Regex patterns
grep supports POSIX basic regex by default. Use -E for extended regex (alternation, +, ?) or -P for Perl regex (lookahead, \d, \w).
# Basic regex (BRE)
grep "error[0-9]" app.log # char class
grep "^Error" app.log # start of line
grep "\.js$" file.txt # end of line
grep "col.ur" file.txt # any char (. = wildcard)
# Extended regex (-E or egrep)
grep -E "error|warning" app.log # alternation
grep -E "colou?r" file.txt # optional u
grep -E "go{2,}" file.txt # 2+ o's (goo, gooo…)
grep -E "https?://" urls.txt # http or https
# Perl-compatible regex (-P)
grep -P "\d{3}-\d{4}" contacts.txt # phone numbers
grep -P "^\s*$" file.txt # blank/whitespace-only lines
grep -P "(?<=Bearer )\S+" log.txt # lookbehind (tokens)
grep -P "\b(foo|bar)\b" file.txt # word boundary alternation
Character classes
# POSIX classes
grep "[[:alpha:]]" file.txt # any letter
grep "[[:digit:]]" file.txt # any digit (0-9)
grep "[[:alnum:]]" file.txt # letter or digit
grep "[[:space:]]" file.txt # whitespace
grep "[[:upper:]]" file.txt # uppercase letter
grep "[[:lower:]]" file.txt # lowercase letter
grep "[[:punct:]]" file.txt # punctuation
# Ranges
grep "[a-z]" file.txt
grep "[A-Z0-9]" file.txt
grep "[^0-9]" file.txt # NOT a digit
Context lines
# 3 lines after the match
grep -A 3 "Exception" app.log
# 3 lines before
grep -B 3 "Exception" app.log
# 3 lines before and after
grep -C 3 "Exception" app.log
# Useful: find a function definition + body
grep -A 20 "^function processPayment" src/payments.js
Output control
# Only print matching part (not whole line)
grep -o "https\?://[^ ]*" file.txt
# Stop after first 5 matches
grep -m 5 "error" app.log
# Quiet mode — useful in scripts
if grep -q "error" app.log; then
echo "Errors found"
fi
# Colorize output
grep --color=auto "error" app.log
# Add to ~/.bashrc:
alias grep='grep --color=auto'
# Null-separated output (safe for filenames with spaces)
grep -rlZ "TODO" . | xargs -0 wc -l
Fixed string search (no regex)
# -F treats pattern as literal string, not regex
grep -F "user.name" file.txt # finds "user.name" not "userXname"
grep -F "$HOME" script.sh # finds literal "$HOME"
grep -F "^D" data.txt # literal "^D", not start-of-line
Multiple patterns
# Match either pattern (-E with |)
grep -E "error|warning|fatal" app.log
# Match multiple fixed strings (-F with |)
grep -F -e "error" -e "warning" -e "fatal" app.log
# From a file of patterns (one per line)
cat patterns.txt
# error
# warning
# fatal
grep -f patterns.txt app.log
# AND logic: pipe two greps
grep "error" app.log | grep "database"
Whole word and whole line
# Whole word: won't match "errors" or "prerror"
grep -w "error" app.log
# Whole line: entire line must match exactly
grep -x "404 Not Found" status.log
# Combine: case-insensitive whole word
grep -iw "todo" src/main.ts
File listing flags
# Files that HAVE matches
grep -rl "TODO" ./src
# Files that DON'T have matches (no TODO)
grep -rL "TODO" ./src
# Show filename always (even single file)
grep -H "error" app.log
# Never show filename
grep -rh "TODO" ./src
Practical patterns
Find all TODO/FIXME in codebase
grep -rn "TODO\|FIXME\|HACK\|XXX" . \
--include="*.ts" \
--exclude-dir=node_modules \
--exclude-dir=.git
Search Git-tracked files only
git grep "console.log"
git grep -n "TODO"
git grep -l "password"
Find large or complex functions (heuristic)
# Functions with many lines — find starts
grep -n "^function \|^ function " src/*.js
# Find all exported functions
grep -rn "^export function\|^export const" src/
Log analysis
# Errors in last hour (if logs have timestamps)
grep "$(date -d '1 hour ago' '+%Y-%m-%d %H')" app.log | grep -i "error"
# Count errors by type
grep -oE "(ERROR|WARN|INFO)" app.log | sort | uniq -c | sort -rn
# Unique IPs from access log
grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" access.log | sort -u
# Find slow requests (> 1000ms)
grep -E "ms=[0-9]{4,}" access.log
Validate config / find missing values
# Find empty values in .env style files
grep -E "^\w+=\s*$" .env.example
# Find hard-coded API keys (rough heuristic)
grep -rE "api[_-]?key\s*=\s*['\"][a-zA-Z0-9]{20,}" . \
--exclude-dir=node_modules
# Find debug flags left on
grep -rn "debug\s*=\s*true\|DEBUG\s*=\s*1" . --include="*.{json,yaml,yml,env}"
Code review helpers
# All console.log left in code
grep -rn "console\.\(log\|warn\|error\|debug\)" src/ --include="*.ts"
# Hard-coded localhost URLs
grep -rn "localhost:[0-9]\+" src/ --include="*.{ts,tsx,js}"
# Missing error handling (async functions without try/catch)
grep -n "async " src/api/*.ts | grep -v "try\|catch"
Pipeline integration
# With xargs
grep -rl "oldFunction" src/ | xargs sed -i 's/oldFunction/newFunction/g'
# With awk for field extraction
grep "POST /api" access.log | awk '{print $7}' | sort | uniq -c | sort -rn
# With sort and uniq
grep -h "Error:" *.log | sort | uniq -c | sort -rn | head -20
# Highlight matches in less
grep -E "error|$" app.log | less -R
# Count non-empty lines
grep -c "." file.txt
Exit codes
grep exits with a code that scripts can check:
| Exit code | Meaning |
|---|---|
0 |
Match found |
1 |
No match |
2 |
Error (file not found, bad regex, etc.) |
# In shell scripts
grep -q "error" app.log && echo "Found errors!" || echo "No errors"
# In CI: fail if debug code left in
grep -rq "console.log" src/ && { echo "Remove console.log before commit"; exit 1; }
grep vs ripgrep (rg)
ripgrep (rg) is a faster alternative that respects .gitignore by default.
| Task | grep | ripgrep |
|---|---|---|
| Basic search | grep -r "pat" . |
rg "pat" |
| Case-insensitive | grep -ri "pat" . |
rg -i "pat" |
| File type filter | --include="*.ts" |
-t ts |
| Respect .gitignore | manual --exclude-dir |
automatic |
| Speed | baseline | 5–10× faster |
| PCRE | -P |
-P |
| Fixed string | -F |
-F |
Use grep when it's already available (scripts, containers). Use rg for daily interactive use.
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
grep "user.name" file |
. matches any char, finds "userXname" |
grep -F "user.name" or grep "user\.name" |
grep "error|warning" with BRE |
| is BRE alternation — not all systems support it |
grep -E "error|warning" |
grep -r pattern dir/ without exclude |
Searches node_modules, .git, etc. | Add --exclude-dir=node_modules --exclude-dir=.git |
grep "^" to count lines |
Matches blank lines too | Use wc -l or grep -c "." for non-empty |
Forgetting -P for \d, \w |
BRE/ERE don't support \d |
Use -P or [0-9] / [a-zA-Z0-9_] |
grep -l then editing in place |
-l gives filenames, but xargs grep re-reads |
Use xargs sed -i after grep -rl |
| Unquoted pattern with spaces | Shell splits the pattern | Always quote: grep "two words" file |
6 FAQ
Q: How do I search for a string that starts with a dash (-)?
Use -- to end options: grep -- "-v" file.txt, or use \-: grep "\-v" file.txt.
Q: How do I search for a newline inside grep?
grep works line by line — it can't match across lines. Use pcregrep -M or awk for multi-line patterns.
Q: What's the difference between -E, -P, and basic grep?
Basic regex (BRE): grep. Extended regex — adds +, ?, |, {} without backslash: grep -E or egrep. Perl regex — adds \d, \w, lookahead/behind: grep -P. Use -E for most cases; -P for advanced patterns.
Q: How do I grep for a tab character?grep $'\t' file.txt (using ANSI-C quoting in bash), or grep -P "\t" file.txt.
Q: Why does grep -r miss some files?
It skips binary files by default. Use -a to treat binary as text. Also check --include/--exclude filters.
Q: How do I make grep output machine-readable?
Use -o for only the match, -h to suppress filenames, -Z for null-separated filenames (safe for xargs -0), and --no-messages to silence errors.