grep๐
Part of a deep dive and a pathway: Text & Pipelines Debugging With Nothing But a Terminal
Consult the map
-
Text & Pipelines โ step 2 of 2
โ Pipes and Redirection ยท you are here ยท (last step) โ
-
Debugging With Nothing But a Terminal โ step 4 of 20
โ Pipes and Redirection ยท you are here ยท Regular Expressions for SREs โ
Every production incident eventually comes down to the same question: what does the log say? And in Linux, the answer almost always involves grep. Whether you're searching a single file, recursively hunting through a config directory, or filtering live log output from journalctl, grep is the tool that cuts through the noise.
Learning to use grep well isn't about memorizing flags. It's about pattern thinking โ knowing how to describe what you're looking for precisely enough that grep can find it.
Where You Might Have Seen This๐
You've used grep-equivalent tools without knowing it:
- IDE find-in-files (VS Code Ctrl+Shift+F, IntelliJ's "Find in Path") โ same concept: search text matching a pattern across multiple files
- SQL
WHERE column LIKE '%pattern%'โ same concept: filter rows by pattern match - Splunk/Elasticsearch/Datadog search โ the search bar is a UI layer over the same operation: find log lines matching a pattern
- Browser Ctrl+F โ single-file version
What grep adds over all of these: it's composable. You can pipe output into grep to filter it, or pipe grep's output out to further tools. That's what makes it the backbone of Linux investigation โ it's not just a search tool, it's a filter that fits anywhere in a pipeline.
The regular expression syntax in grep -E is the same regex you use in Python, JavaScript, Java, Go, or any language. Learning it here pays off everywhere.
The Core Idea๐
grep reads input line by line and prints lines that match a pattern. That's it. Its power comes from:
- Flexible pattern language (regular expressions)
- Composability with other commands via pipes
- A rich set of flags for controlling what and how it matches
grep "error" /var/log/syslog # (1)!
journalctl | grep "Failed" # (2)!
grep -r "database_host" /etc/myapp/ # (3)!
- Search a file.
- Filter a stream.
- Search recursively.

That second one โ filtering a stream โ is what makes grep compose with everything else on this page. It sits in the middle of a pipeline as easily as at the start:
graph LR
Source["cat access.log"] -->|pipe| Grep["grep '500'"]
Grep -->|pipe| Awk["awk '{print $1}'"]
Awk -->|pipe| Sort["sort | uniq -c"]
style Source fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style Grep fill:#d69e2e,stroke:#cbd5e0,stroke-width:2px,color:#000
style Awk fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
style Sort fill:#2d3748,stroke:#cbd5e0,stroke-width:2px,color:#fff
Essential Flags๐
-
Matching Control
What to Matchgrep -i "error" logfile # (1)! grep -v "debug" logfile # (2)! grep -w "fail" logfile # (3)! grep -F "literal.string" file # (4)! grep -E "pattern1|pattern2" file # (5)!- Case-insensitive (
-i). - Invert โ exclude matching lines (
-v). - Whole word only (
-w) โ won't match "failure". - Treat the pattern as literal, not regex (
-F). - Extended regex โ OR matching (
-E).
- Case-insensitive (
-
Output Format
Controlling Outputgrep -n "error" logfile # (1)! grep -c "error" logfile # (2)! grep -l "error" /var/log/*.log # (3)! grep -L "error" /etc/*.conf # (4)! grep -o "pattern" logfile # (5)! grep -h "error" /var/log/*.log # (6)!- Show line numbers (
-n). - Count of matching lines (
-c). - Show only filenames (
-l). - Files that do not match (
-L). - Print only the matched part (
-o). - Suppress the filename in output (
-h).
- Show line numbers (
-
Context
Show Surrounding Linesgrep -A 3 "error" logfile # (1)! grep -B 3 "error" logfile # (2)! grep -C 3 "error" logfile # (3)!- 3 lines after the match (
-A). - 3 lines before the match (
-B). - 3 lines on both sides (
-C, context).
Key insight: Context flags are essential for log analysis. An error line without context is often meaningless โ you need to see what happened before and after.
- 3 lines after the match (
-
Recursive Search
Search Directoriesgrep -r "pattern" /etc/ # (1)! grep -rl "pattern" /etc/ # (2)! grep -ri "pattern" /etc/ # (3)! grep -r "pattern" --include="*.conf" /etc/ # (4)! grep -r "pattern" --exclude="*.log" /var/ # (5)!- Search all files recursively (
-r). - Filenames only, recursive (
-rl). - Recursive + case-insensitive (
-ri). - Limit to
.conffiles. - Skip log files.
- Search all files recursively (
Regular Expressions๐
grep's default mode uses Basic Regular Expressions (BRE). With -E (or egrep), you get Extended Regular Expressions (ERE) โ more powerful and the version you'll use most.
Anchors and Wildcards๐
grep "^error" logfile # (1)!
grep "error$" logfile # (2)!
grep "^$" logfile # (3)!
grep "^#" /etc/nginx.conf # (4)!
grep -v "^#\|^$" /etc/nginx.conf # (5)!
- Lines starting with "error".
- Lines ending with "error".
- Empty lines.
- Comment lines only.
- Exclude comments and blank lines.
grep "err.r" logfile # (1)!
grep "err.*log" logfile # (2)!
grep "colou\?r" logfile # (3)!
grep -E "colou?r" logfile # (4)!
.matches any single character..*matches any sequence of characters.\?makes the previous character optional (BRE) โ matches "color" and "colour".- ERE equivalent โ no backslash needed.
Character Classes๐
grep "[0-9]" logfile # (1)!
grep "[a-zA-Z]" logfile # (2)!
grep "[[:digit:]]" logfile # (3)!
grep "[[:alpha:]]" logfile # (4)!
grep "[[:space:]]" logfile # (5)!
grep "[^0-9]" logfile # (6)!
- Any digit.
- Any letter.
- POSIX class: any digit.
- POSIX class: any letter.
- POSIX class: whitespace.
^inside[]means NOT โ any non-digit.
Extended Regex: Quantifiers and Alternation๐
With -E, you get cleaner syntax and more quantifiers:
grep -E "error|warning|critical" logfile # (1)!
grep -E "err+" logfile # (2)!
grep -E "err?" logfile # (3)!
grep -E "err{2}" logfile # (4)!
grep -E "err{2,4}" logfile # (5)!
grep -E "(ERROR|error): (disk|memory)" logfile # (6)!
grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" logfile # (7)!
- Alternation โ match any of these patterns.
+= one or more of the previous character.?= zero or one of the previous character.{n}= exactly n repetitions.{n,m}= between n and m repetitions.- Group and alternate.
- Matches lines starting with an IPv4 address pattern: 1โ3 digits then a dot, four times. Not a perfect IP validator, but useful for extracting IP lines from logs.
Fixed String Mode (-F)๐
When your search pattern contains regex special characters (., *, [, (, etc.) that you want to treat literally, use -F:
grep -F "2.5.0.1" /etc/hosts # (1)!
grep -F "user[admin]" config.txt # (2)!
grep -F "price: $9.99" catalog.txt # (3)!
- Searches for the literal
2.5.0.1, not a regex โ the dots are treated literally. - Literal brackets, not a character class.
- Literal
$and..
Without -F, grep "2.5.0.1" would match "250001" (the dots match any character). Use -F when searching for version strings, IP addresses, file paths, or any literal text.
grep in the Real World๐
Log Analysis Patterns๐
journalctl --since "1 hour ago" | grep -i "error\|fail\|critical" -A 2 # (1)!
grep -oE "(ERROR|WARNING|CRITICAL)" /var/log/myapp/app.log | sort | uniq -c | sort -nr # (2)!
grep "user=jsmith" auth.log | grep "FAILED" # (3)!
grep -E " (4[0-9]{2}|5[0-9]{2}) " /var/log/nginx/access.log # (4)!
grep "Restart" /var/log/syslog | tail -1 # (5)!
grep -B 5 -A 10 "OutOfMemoryError" /var/log/myapp/app.log # (6)!
- Find errors from the last hour, with 2 lines of trailing context.
- Count errors by type, most frequent first.
- Find lines matching multiple patterns (must match all).
- Find specific HTTP status codes (4xx/5xx) in the access log.
- Find the last occurrence of a pattern.
- Show context around an error for diagnosis.
Configuration File Analysis๐
grep -v "^#\|^$" /etc/ssh/sshd_config # (1)!
grep -w "PermitRootLogin" /etc/ssh/sshd_config # (2)!
grep -rl "db-prod-01" /etc/ # (3)!
grep -v "^#" /etc/nginx/nginx.conf | grep -i "listen" # (4)!
grep -r "ssl_certificate" /etc/nginx/ # (5)!
- Show the effective config โ exclude comments and blank lines.
- Check if a specific setting is enabled (
-wmatches whole words). - Find which config files reference a hostname.
- Find all uncommented
listendirectives. - Check for a setting across multiple config files.
Process and System Filtering๐
ps aux | grep "[n]ginx" # (1)!
df -h | grep -v "tmpfs\|devtmpfs\|udev" # (2)!
systemctl list-units | grep "failed" # (3)!
ss -tlnp | grep "LISTEN" # (4)!
grep -v "nologin\|/bin/false" /etc/passwd | cut -d: -f1 # (5)!
- The bracket trick
[n]ginxmatches "nginx" but not thegrepprocess itself โgrepshows up inpsas[n]ginx, which doesn't match its own pattern. A common solution to the "grep shows itself" problem. - Filter
dfoutput to real filesystems only. - Find services in a specific (failed) state.
- Check which ports are listening.
- Find users with real login shells.
Common Scenarios๐
You know something's wrong but not what. Start broad, narrow down.
# Step 1: Find recent errors
journalctl --since "1 hour ago" | grep -i "error\|fail" | head -30
# Step 2: Find the specific service's logs
journalctl -u nginx --since "1 hour ago" | grep -i "error" -B 3 -A 5
# Step 3: If it's a file-based log, same approach
grep -i "error" /var/log/nginx/error.log | tail -50
# Step 4: Once you spot an error, get more context
grep -n "upstream timed out" /var/log/nginx/error.log | tail -5
# note the line number, then:
grep -A 20 "upstream timed out" /var/log/nginx/error.log | tail -25
After editing config files, verify what's actually active:
# See all non-comment, non-empty lines
grep -v "^[[:space:]]*#\|^[[:space:]]*$" /etc/nginx/nginx.conf
# Check a specific setting across all included files
grep -r "worker_processes" /etc/nginx/
# Verify SSL settings
grep -r "ssl_" /etc/nginx/ | grep -v ".conf:#"
# Compare two config files
grep -F -x -f expected.conf actual.conf # lines in both files
Looking for signs of unauthorized access or misconfiguration:
# Failed SSH login attempts
grep "Failed password" /var/log/auth.log | tail -50
# Successful logins from unexpected IPs
grep "Accepted password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c
# World-writable files (via find output)
find /etc -type f 2>/dev/null | xargs grep -l "" 2>/dev/null | head -5
# Check for root logins
grep "session opened for user root" /var/log/auth.log
# Find cron jobs being added
grep "CRON" /var/log/syslog | grep "CMD" | tail -20
You know a setting exists somewhere, but not which file:
# Which file in /etc references a database hostname?
grep -rl "db-prod-01" /etc/ 2>/dev/null
# Which config file sets a specific IP?
grep -r "192.168.10.50" /etc/ 2>/dev/null
# Which log files contain a specific error code?
grep -rl "ORA-01017" /var/log/ 2>/dev/null
# Once you find the file, get context
grep -n "db-prod-01" /etc/myapp/config.yml -B 2 -A 2
Quick Reference๐
Essential Flag Combinations๐
| Pattern | Use Case |
|---|---|
grep -i "error" file |
Case-insensitive search |
grep -v "debug" file |
Exclude lines matching pattern |
grep -c "error" file |
Count matches |
grep -n "error" file |
Show line numbers |
grep -l "error" *.log |
Filenames with matches |
grep -r "setting" /etc/ |
Recursive directory search |
grep -rl "setting" /etc/ |
Recursive, filenames only |
grep -A 5 "error" file |
Match plus 5 lines after |
grep -B 5 "error" file |
Match plus 5 lines before |
grep -C 5 "error" file |
Match plus 5 lines each side |
grep -E "a\|b" file |
Match either pattern |
grep -w "fail" file |
Whole word only |
grep -F "1.2.3.4" file |
Literal string (no regex) |
grep -v "^#\|^$" file |
Remove comments and blank lines |
Common Regex Patterns๐
| Pattern | Matches |
|---|---|
^word |
Lines starting with "word" |
word$ |
Lines ending with "word" |
^$ |
Empty lines |
word. |
"word" followed by any character |
word.*end |
"word" then anything then "end" |
[0-9] |
Any digit |
[[:digit:]] |
Any digit (POSIX) |
-E "a\|b" |
Either "a" or "b" |
-E "err+" |
"er" then one or more "r" |
-E "[0-9]{3}" |
Exactly three digits |
Practice Exercises๐
Exercise 1: Multi-Flag Combination
In /var/log/auth.log (or /var/log/secure):
- Find all lines containing "Failed" (case-insensitive)
- Show the 2 lines after each match for context
- Count how many total matches there are
Exercise 2: Config File Cleanup
Show the "effective" configuration from /etc/ssh/sshd_config โ only lines that are actually set (no comment lines, no blank lines).
Exercise 3: Process Filter
List all running processes containing "python" in their command, but exclude the grep process itself from the results.
Exercise 4: Find Files Containing Multiple Patterns
Find all .conf files in /etc/ that contain both the word "ssl" AND the word "certificate".
Solution
grep -rl "ssl" /etc/ --include="*.conf" 2>/dev/null \
| xargs grep -l "certificate" 2>/dev/null
First grep -rl finds files containing "ssl". Then xargs grep -l filters those results to only files also containing "certificate". The two-stage approach handles AND logic, which grep alone can't do directly.
Quick Recap๐
- Default grep: BRE patterns, case-sensitive, prints matching lines
-E: Extended regex for|,+,?,{n}without backslashes โ use this almost always-i: Case-insensitive;-v: invert;-w: whole word;-F: literal string-r: recursive directory search;-l: filenames only;-n: line numbers-A/-B/-C: context lines โ essential for log analysis-c: count;-o: print only matched part- Config trick:
grep -v "^#\|^$"removes comments and blank lines โ use constantly - Process trick:
[p]atternin brackets prevents grep from matching itself
What's Next?๐
You can search text. Now you need to understand what's running on the system โ which processes exist, what resources they consume, and how to manage them.
Head to Processes to learn how Linux manages running programs: process IDs, signals, process states, and the commands every sysadmin uses to investigate and control what's happening on a system.
If you're following the Debugging With Nothing But a Terminal pathway, the next step is Regular Expressions for SREs โ the six characters that solve most log-searching problems, and why some of them can take down a production system.
Further Reading๐
Command References๐
man grepโ complete reference including all flags and regex syntaxman 7 regexโ POSIX regular expression specificationman egrepโ extended grep (same asgrep -E)
Deep Dives๐
- Regular Expressions Info โ comprehensive regex tutorial and reference
- The Art of Command Line: Data Wrangling โ grep patterns in real workflows
- Grep Cookbook โ practical grep examples
Official Documentation๐
- GNU grep Manual โ the authoritative grep reference
- Red Hat: grep Command Examples โ RHEL-specific grep guide
- Arch Wiki: grep โ concise grep reference with examples