Free Ebook cover Linux Command Line for Beginners: Navigate, Search, and Automate Simple Tasks

Linux Command Line for Beginners: Navigate, Search, and Automate Simple Tasks

New course

10 pages

Searching and Filtering Text with grep and Friends

Capítulo 6

Estimated reading time: 9 minutes

+ Exercise

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' FILE

Example: find lines mentioning “timeout” in a config file:

grep 'timeout' /etc/myapp/app.conf

grep 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 App

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.log

By 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.conf

You 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.log

On 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.log

Character 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.log

In 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.log

Here, [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.log

Alternation: match one of several options

Alternation uses | and requires grep -E:

grep -E 'timeout|refused|unreachable' /var/log/app.log

This 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:

  • -print0 and xargs -0 safely handle filenames with spaces.
  • -n adds 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.log

To see which files contain matches (useful in a directory of rotated logs), use -l:

grep -r -l -i 'fatal' ./logs

To show matches with surrounding context, use:

  • -C N for N lines before and after
  • -B N for before only
  • -A N for after only
grep -n -i -C 2 'panic' /var/log/app.log

Mini-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/logs
cat > ~/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
EOF
cat > ~/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
EOF
cat > ~/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
EOF

Step 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/logs

What to look for:

  • -r searches all files under the directory.
  • -n shows line numbers so you can reference exact entries.
  • -E enables 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/logs

If 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-*.log

Try 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-*.log

Step 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-*.log

Explanation:

  • [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.txt

Verify what you wrote:

grep -n '' ~/grep-lab/report/filtered-report.txt

The 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-*.log

This 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

Now answer the exercise about the content:

You want to find only ERROR or FATAL lines across all files under ~/grep-lab/logs and include line numbers. Which command best fits this goal?

You are right! Congratulations, now go to the next page

You missed! Try again.

-r searches recursively in the directory, -n adds line numbers, and -E enables alternation with | to match either ERROR or FATAL.

Next chapter

Pipes and Redirection: Building Command Line Workflows

Arrow Right Icon
Download the app to earn free Certification and listen to the courses in the background, even with the screen off.