I built precise extended regular expressions with grep to extract IPs, error codes, and tokens from logs and source. Anchoring patterns and using character classes turned broad, false-positive-heavy searches into exact matches.

Objective & Context

Regex is the universal text-matching language across grep, sed, and code. This lab develops ERE fluency, anchors, classes, quantifiers, and groups, the precision tool behind log triage and the Python string lab.

Learning Objectives

  • Write ERE patterns with anchors, classes, quantifiers, and groups.
  • Extract matches with grep -o and count them accurately.
  • Anchor patterns to line starts, ends, and word boundaries.
  • Search case-insensitively with context lines using -i and -C.
  • Choose between basic, extended, and Perl regex modes deliberately.

Environment & Prerequisites

Prerequisites

  • Linux with GNU grep (supports -E and -P; verify with grep --version).
  • Sample logs containing IP addresses and HTTP status codes.
  • A regex reference for ERE syntax (kept open while you practice).
  • Familiarity with the file filters lab helps.

Step-by-Step Execution

1. Match IPv4 addresses

grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort -u

Expected output:

198.51.100.7
203.0.113.45

What this teaches: The -o flag prints only the matched portion; -E enables extended syntax so {1,3} and groups work without backslashes; sort -u deduplicates the extracted addresses.

2. Anchor to whole-line status codes

grep -E ' (4[0-9]{2}|5[0-9]{2}) ' access.log

Expected output:

203.0.113.45 - - [17/Jun/2026:09:40:11] "GET /old-page HTTP/1.1" 404 512
198.51.100.7 - - [17/Jun/2026:09:41:02] "POST /api/login HTTP/1.1" 503 128

What this teaches: Spaces around the group anchor the match to the status field so a 404 inside a URL or timestamp cannot false-positive; alternation (4xx|5xx) covers both error classes in one pattern.

3. Case-insensitive context search

grep -iC2 'failed password' /var/log/auth.log

Expected output:

Jun 17 09:44:10 srv01 sshd[2101]: Failed password for invalid user admin
Jun 17 09:44:10 srv01 sshd[2101]: from 203.0.113.45 port 51234
Jun 17 09:44:11 srv01 sshd[2101]: Failed password for invalid user admin
--
Jun 17 09:44:12 srv01 sshd[2101]: from 198.51.100.9 port 60112

What this teaches: -i ignores case so FAILED and failed both match; -C2 prints two lines of context around each hit, revealing the source IP that a bare match would hide.

How It Works

Regular expressions describe a set of strings, and the engine tests input against that description left to right. Anchors (^, $) pin the match to positions, character classes ([0-9]) define allowed symbols at one position, and quantifiers ({1,3}, *, +) control repetition. Groups with parentheses both group alternations and capture submatches for backreferences.

grep offers three dialects: basic (BRE, the default, where + and ? need backslashes), extended (ERE with -E, the modern readable default), and Perl-compatible (PCRE with -P, adding lookarounds and \d shorthands). Choosing -E deliberately avoids the backslash soup of BRE while staying portable, since -P is a GNU extension other greps may lack.

Precision matters because logs are adversarial: IPs appear inside longer digit runs, status codes hide in URLs, and case varies. Anchoring to field boundaries, extracting with -o, and validating against known-bad lines turns regex from guesswork into a reliable filter you can pipe into the frequency idioms from the filters lab.

Validation & Testing

Run the IP and status-code patterns against a known log and confirm matches against a manual count, checking for false positives. Pass criteria: patterns match exactly the intended tokens with anchors preventing partial or stray matches.

Troubleshooting

SymptomLikely causeFix
Pattern with + or ? matches literallyBRE is active; those are literal characters without backslashesUse grep -E for extended syntax
grep -P warns it is experimental or missingPCRE support is a GNU extension, absent on BSD/macOS grepRewrite with -E, or install pcre2grep for portability
Match includes trailing characters you did not wantA greedy quantifier consumed more than intendedAnchor the end ($ or a delimiter) or use a negated class like [^ ]*
. matched any character instead of a dotDot is a metacharacter meaning any characterEscape it as \. when you mean a literal period
-o output has duplicates inflating countsOverlapping or repeated matches on one line each printPipe through sort -u when you need distinct values
Case variations slip throughPattern assumed one casingAdd -i, or write explicit classes like [Ff]ailed
Advanced: Troubleshooting
  • Too many matches: anchor with ^/$ or word boundaries to tighten scope.
  • BRE vs ERE: use -E for +/?/| without backslashes.
  • Greedy capture: grep is line-oriented; for multiline or lazy matching use -P (PCRE).

Key Results

  • Extracted unique IPs from logs with one anchored pattern.
  • Isolated 4xx/5xx errors precisely with alternation.
  • Reduced false positives by anchoring and using classes.
  • Built reusable patterns shared with the sed/awk lab.

Key Takeaways

  • Use grep -E for readable extended regex; reserve -P for lookarounds.
  • Anchor patterns to field boundaries to kill false positives.
  • -o extracts matches; -c counts lines, not occurrences.
  • Escape literal dots and pin quantifier ranges to avoid greedy surprises.
  • Context flags (-A/-B/-C) turn isolated hits into actionable evidence.

Practice Challenges

Challenge 1: Extract clean IPv4s

Goal: pull only valid dotted-quad IPs from a noisy log without matching version strings like 1.2.3.4.5 or timestamps.

Hint: anchor each octet group with word boundaries or surrounding delimiters.

Challenge 2: Brute-force hunter

Goal: find source IPs with more than 10 failed SSH passwords in auth.log using grep plus the uniq -c frequency chain.

Hint: extract the IP field from failed password lines first.