Why grep Matters for Configs and Logs
When you troubleshoot a service, you often need to find a specific setting in a configuration file or a specific event in a log. grep searches text for lines that match a pattern and prints the matching lines. This makes it ideal for quickly locating errors, warnings, IP addresses, usernames, feature flags, and other signals hidden inside large files.
In this chapter you will practice: searching single files and whole directories, controlling case sensitivity, showing line numbers, excluding matches, using extended regular expressions, and combining grep with other commands to build useful filtered outputs.
grep Basics
Basic syntax
The most common form is:
grep 'PATTERN' FILEExample: find lines mentioning “timeout” in a config file:
grep 'timeout' /etc/myapp/app.confgrep prints entire lines that contain the pattern. If nothing matches, it prints nothing and exits with a non-zero status (useful in scripts).
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Search multiple files
You can provide multiple files and grep will search them all:
grep 'ERROR' /var/log/app.log /var/log/app.old.logBy default, when multiple files are searched, grep prefixes each matching line with the filename.
Common Flags You Will Use Constantly
-n: show line numbers
Line numbers are essential when you need to edit a config or reference a specific log entry:
grep -n 'listen_port' /etc/myapp/app.conf-i: ignore case
Logs are often inconsistent in capitalization (ERROR vs Error vs error). Use -i to match regardless of case:
grep -i 'error' /var/log/app.log-r: recursive search through directories
Use -r to search all files under a directory (great for a directory of logs or a directory of configs):
grep -r 'db_host' /etc/myapp/Tip: If you only want certain file types, combine with shell globs by targeting a subpath pattern when possible (for example, /var/log/myapp/*.log) or use find later in this chapter.
-v: invert match (show lines that do NOT match)
-v is a filtering tool: it removes noise. Example: show lines that are not comments (lines starting with #) in a config:
grep -v '^#' /etc/myapp/app.confYou can also remove known “healthy” messages from logs to focus on unusual lines:
grep -v 'healthcheck ok' /var/log/app.log-E: extended regular expressions
Basic grep supports simple patterns, but many useful regex features (like | alternation) are easier with extended regex. Use -E when your pattern needs it:
grep -E 'ERROR|FATAL' /var/log/app.logOn many systems you may also see egrep, which is equivalent to grep -E.
Simple Regular Expressions You Can Apply Immediately
Regular expressions (regex) let you describe patterns rather than exact words. For beginners, focus on a small set that covers many real tasks: anchors, character classes, and alternation.
Anchors: match start or end of line
^matches the start of a line.$matches the end of a line.
Examples:
- Find lines that start with “ERROR” (common in structured logs):
grep '^ERROR' /var/log/app.log- Find lines that end with “failed”:
grep 'failed$' /var/log/app.logCharacter classes: match one character from a set
Character classes are written in square brackets [...] and match a single character. Examples:
- Match “error” or “errors” by allowing an optional “s” using a simple trick (two patterns with alternation is clearer, but this shows character classes):
grep -E 'errors?' /var/log/app.logIn extended regex, ? means “optional” (zero or one). If you prefer to avoid ? for now, use alternation: error|errors.
- Match an HTTP status code like 400–499 (client errors) in access logs:
grep -E ' 4[0-9][0-9] ' /var/log/nginx/access.logHere, [0-9] matches any digit. The pattern 4[0-9][0-9] matches 400 through 499.
- Match either “WARN” or “WARNING” by matching a single character choice (simple example):
grep -E 'WARN(ING)?' /var/log/app.logAlternation: match one of several options
Alternation uses | and requires grep -E:
grep -E 'timeout|refused|unreachable' /var/log/app.logThis is very useful for building a “watch list” of error keywords.
Combining grep with Other Commands
grep becomes more powerful when you use it in pipelines. The idea is: one command produces text, and grep filters it.
Filter command output
Example: show only lines containing “listen” from a command that prints configuration:
some_command --print-config | grep -i 'listen'Chain multiple greps to narrow results
You can apply multiple filters by piping grep into another grep. Example: find error lines that mention a specific host:
grep -i 'error' /var/log/app.log | grep 'db01'This is easy to read and works well for ad-hoc investigation.
Use grep with find to search specific files
Recursive grep is convenient, but sometimes you want more control over which files are searched (by name, age, or location). A common pattern is: use find to list files, then pass them to grep.
Example: search only .log files under a directory:
find ./logs -type f -name '*.log' -print0 | xargs -0 grep -n -i 'error'Notes:
-print0andxargs -0safely handle filenames with spaces.-nadds line numbers, which helps when you need to reference exact entries.
Count matches and build quick summaries
To count matching lines, use -c (this is often used for quick health checks):
grep -c -i 'error' /var/log/app.logTo see which files contain matches (useful in a directory of rotated logs), use -l:
grep -r -l -i 'fatal' ./logsTo show matches with surrounding context, use:
-C Nfor N lines before and after-B Nfor before only-A Nfor after only
grep -n -i -C 2 'panic' /var/log/app.logMini-Lab: Investigate Sample Logs and Create a Filtered Report
This mini-lab builds a realistic workflow: search a directory of logs for error patterns, extract matching lines with context, then create a small report file you can share.
Step 1: Create a sample log directory
Run the following to create a practice directory with a few log files:
mkdir -p ~/grep-lab/logscat > ~/grep-lab/logs/app-2026-01-14.log <<'EOF'
2026-01-14T09:00:01Z INFO service=api msg="started" port=8080
2026-01-14T09:02:10Z INFO service=api msg="healthcheck ok"
2026-01-14T09:05:44Z WARN service=api msg="slow request" path=/v1/items latency_ms=1200
2026-01-14T09:06:01Z ERROR service=api msg="db connection refused" host=db01
2026-01-14T09:06:02Z INFO service=api msg="retrying" attempt=1
2026-01-14T09:06:05Z ERROR service=api msg="db connection refused" host=db01
EOFcat > ~/grep-lab/logs/app-2026-01-15.log <<'EOF'
2026-01-15T10:11:00Z INFO service=api msg="request" path=/v1/items status=200
2026-01-15T10:12:31Z ERROR service=api msg="timeout" upstream=payments
2026-01-15T10:12:33Z ERROR service=api msg="timeout" upstream=payments
2026-01-15T10:13:02Z INFO service=api msg="healthcheck ok"
2026-01-15T10:14:18Z FATAL service=api msg="panic: nil pointer" trace_id=abc123
EOFcat > ~/grep-lab/logs/access-2026-01-15.log <<'EOF'
10.0.0.5 - - [15/Jan/2026:10:11:00 +0000] "GET /v1/items HTTP/1.1" 200 512
10.0.0.8 - - [15/Jan/2026:10:12:00 +0000] "GET /v1/items HTTP/1.1" 404 169
10.0.0.9 - - [15/Jan/2026:10:12:05 +0000] "POST /v1/pay HTTP/1.1" 502 1024
10.0.0.5 - - [15/Jan/2026:10:12:10 +0000] "GET /v1/items HTTP/1.1" 500 2048
EOFStep 2: Search the directory for error patterns
Start broad: find lines containing common severity keywords across all logs:
grep -r -n -E 'ERROR|FATAL|WARN' ~/grep-lab/logsWhat to look for:
-rsearches all files under the directory.-nshows line numbers so you can reference exact entries.-Eenables alternation with|.
Step 3: Focus on “real problems” and remove noise
Suppose you decide that WARN lines are too noisy and you only want ERROR and FATAL:
grep -r -n -E 'ERROR|FATAL' ~/grep-lab/logsIf your logs include repetitive “healthcheck ok” lines and you want to exclude them from a broader search, use -v:
grep -r -n -i 'info|warn|error|fatal' ~/grep-lab/logs | grep -v -i 'healthcheck ok'This demonstrates a common workflow: first include what you might need, then exclude known-noise patterns.
Step 4: Extract matching lines with context
Context helps you understand what happened right before and after an error. For example, show 2 lines before and after each ERROR or FATAL in the application logs:
grep -n -E -C 2 'ERROR|FATAL' ~/grep-lab/logs/app-*.logTry a more targeted investigation: find “timeout” events and show the next line after each match (maybe the next line contains a retry message):
grep -n -A 1 -i 'timeout' ~/grep-lab/logs/app-*.logStep 5: Use simple regex to extract HTTP error responses
In access logs, HTTP 4xx and 5xx status codes are often important. Use character classes to match them:
grep -n -E '" [45][0-9][0-9] ' ~/grep-lab/logs/access-*.logExplanation:
[45]matches either 4 or 5.[0-9][0-9]matches two digits.- The surrounding spaces help avoid accidental matches inside other numbers.
Step 6: Create a filtered report file
Now create a report that collects the most important signals into one file. This example report includes:
- All ERROR and FATAL lines from application logs
- All HTTP 4xx/5xx lines from access logs
Create a report directory and write the report:
mkdir -p ~/grep-lab/report{
echo '=== Application errors (ERROR/FATAL) ==='
grep -r -n -E 'ERROR|FATAL' ~/grep-lab/logs/app-*.log
echo
echo '=== HTTP client/server errors (4xx/5xx) ==='
grep -n -E '" [45][0-9][0-9] ' ~/grep-lab/logs/access-*.log
} > ~/grep-lab/report/filtered-report.txtVerify what you wrote:
grep -n '' ~/grep-lab/report/filtered-report.txtThe pattern '' matches every line, so this is a quick way to print the file with line numbers (useful when you want to reference parts of the report).
Optional practice: tighten patterns with anchors
If your application logs always start with an ISO timestamp, you can anchor patterns to reduce accidental matches. For example, match only lines that begin with a date and contain ERROR or FATAL:
grep -n -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}.*(ERROR|FATAL)' ~/grep-lab/logs/app-*.logThis uses extended regex and a start-of-line anchor. If your system’s grep -E does not support {n} repetition, keep it simple and rely on ^ plus keywords:
grep -n -E '^(2026-01-14|2026-01-15).* (ERROR|FATAL) ' ~/grep-lab/logs/app-*.log