Chapter 05Lesson 02~55 minutes

Searching Text with grep and Regular Expressions

Searching text is a core incident-response and delivery skill. This lesson teaches a reliable progression: start with literal matching, add regular-expression structure only when needed, constrain the input set, and interpret both output and exit status.

BeginnergrepRegular expressions

Learning objectives

By the end of this lesson

  • Distinguish shell globbing from regular-expression matching.
  • Choose fixed-string, basic, or extended regular-expression modes deliberately.
  • Use line numbers, context, recursion, inclusion filters, and match limits.
  • Interpret grep exit statuses correctly in scripts.
  • Search a synthetic multi-service log set while preserving filenames and evidence.

1. grep selects records that match a pattern

grep reads lines and prints those that satisfy a pattern. The pattern is not a shell wildcard. The shell first processes quoting, variables, and filename expansion; then grep interprets the resulting pattern using its selected matching syntax.

From command line to matching records
flowchart TD
  C["Quoted command text"] --> S["Shell parsing and expansion"]
  S --> G["grep receives pattern and inputs"]
  G --> M["Matcher evaluates each record"]
  M --> O["Matching output and exit status"]
Quote the pattern

Use single quotes for a literal regular expression unless you intentionally need shell expansion. An unquoted *, ?, or bracket expression may be expanded into filenames before grep runs.

2. Start with fixed-string matching

When the target is a literal token such as an error code, URL, or configuration key, grep -F treats regular-expression metacharacters as ordinary characters. This reduces accidental complexity and is often faster.

log="$HOME/devops-academy/linux/chapter05/lesson02/orders.log"

# Literal period characters and brackets need no escaping in fixed mode.
grep -F 'version=1.2.3' -- "$log"
grep -F '[health]' -- "$log"

# Search for any fixed pattern listed in a file.
printf '%s\n' 'ERROR' 'CRITICAL' 'panic' > patterns.txt
grep -F -f patterns.txt -- "$log"

Use -i for case-insensitive matching only when case is semantically irrelevant. In identifiers, paths, and structured values, collapsing case may merge distinct data.

3. Build regular expressions from small parts

Regular expressions describe text structure. GNU grep supports basic regular expressions by default, extended regular expressions with -E, and Perl-compatible expressions with -P when the build supports them. Portable operational work usually favors fixed strings, BRE, or ERE.

ElementMeaningExample
^ / $Beginning or end of line'^ERROR' / 'completed$'
.Any single character'api.v1'
[abc]One listed character'node[123]'
[^abc]One character not listed'[^0-9]'
*Zero or more of the preceding expression'ab*c'
+, ?, |ERE repetition and alternatives'ERROR|WARN'
{m,n}Bounded repetition'[0-9]{3}'
# Lines that begin with an ISO-like date and contain ERROR or WARN.
grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}.*level=(ERROR|WARN)' -- "$log"

# Match a whole word rather than a substring such as NOTERROR.
grep -w 'ERROR' -- "$log"

# Match only the selected portion, useful for extraction.
grep -Eo 'request_id=[[:alnum:]-]+' -- "$log"

Locale can affect character classes and case folding. POSIX classes such as [[:digit:]], [[:space:]], and [[:alnum:]] communicate intent more clearly than ad hoc ranges, though exact behavior remains locale-aware.

4. Preserve location and context

A matching line without its source may be insufficient evidence. Use filename and line-number options when searching multiple files, and add bounded context only when it helps explain the event.

logs="$HOME/devops-academy/linux/chapter05/lesson02/logs"

# Always show filename and line number.
grep -Hn -F 'request_id=req-1042' -- "$logs"/*.log

# Show two lines before and three after each match.
grep -Hn -B 2 -A 3 -F 'level=ERROR' -- "$logs"/*.log

# Recursive search constrained to log files and excluding archives.
grep -RIn \
  --include='*.log' \
  --exclude='*.gz' \
  -E 'level=(ERROR|CRITICAL)' \
  -- "$logs"

# Stop after the first match per input file.
grep -m 1 -Hn -F 'startup complete' -- "$logs"/*.log
-r versus -R

Recursive symlink behavior differs. Follow links only when you understand the tree and trust its targets; otherwise recursive searches can cross boundaries or revisit unexpected data.

5. Output and exit status are separate signals

grep conventionally returns status 0 when at least one selected line exists, 1 when no line matches, and 2 for an error. “No match” is often a normal result, not a command failure.

if grep -q -F 'deployment complete' -- "$log"; then
  printf '%s\n' 'Deployment completion record found.'
else
  status=$?
  if (( status == 1 )); then
    printf '%s\n' 'Completion record not found yet.'
  else
    printf 'grep failed with status %d\n' "$status" >&2
    exit "$status"
  fi
fi

-q suppresses normal output and is useful when only the condition matters. Be cautious in pipelines: an early successful exit can close the pipe before the producer finishes, and shell options such as pipefail may expose the producer’s resulting failure.

6. Hands-on lab: investigate a request across services

lab="$HOME/devops-academy/linux/chapter05/lesson02"
logs="$lab/logs"
mkdir -p -- "$logs"

cat > "$logs/gateway.log" <<'EOF'
2026-08-04T10:00:01Z level=INFO request_id=req-1042 route=/orders status=202
2026-08-04T10:00:02Z level=WARN request_id=req-1042 message="upstream slow"
2026-08-04T10:00:04Z level=ERROR request_id=req-1042 message="upstream timeout"
EOF
cat > "$logs/orders.log" <<'EOF'
2026-08-04T10:00:01Z level=INFO request_id=req-1042 action=create
2026-08-04T10:00:03Z level=INFO request_id=req-1042 dependency=payments
2026-08-04T10:00:04Z level=ERROR request_id=req-1042 code=PAYMENT_TIMEOUT
EOF
cat > "$logs/payments.log" <<'EOF'
2026-08-04T10:00:03Z level=INFO request_id=req-1042 provider=demo-pay
2026-08-04T10:00:04Z level=CRITICAL request_id=req-1042 message="provider unavailable"
EOF

printf '%s\n' '=== request timeline ==='
grep -Hn -F 'request_id=req-1042' -- "$logs"/*.log | sort -t: -k3,3

printf '%s\n' '=== severe events ==='
grep -HnE 'level=(ERROR|CRITICAL)' -- "$logs"/*.log

printf '%s\n' '=== extracted error codes ==='
grep -hEo 'code=[[:alnum:]_-]+' -- "$logs"/*.log | sort -u

Verification checklist

7. Common mistakes and stronger habits

Using regex when a literal search is enough

Start with -F. Add regex operators only when the text structure requires them.

Leaving patterns unquoted

The shell may expand metacharacters before grep receives them. Single-quote static patterns.

Treating no match as a fatal error

Status 1 means the input was searched successfully but nothing matched. Handle it as a distinct state.

Searching an unbounded tree

Choose a root, include relevant file types, exclude archives and generated directories, and consider symlink behavior.

8. Knowledge check

Question 1. When is grep -F preferable to regular-expression mode?

Question 2. What do grep statuses 0, 1, and 2 conventionally represent?

Question 3. Why should a pattern such as '*.log' not be confused with a regular expression?

9. Summary

Reliable search starts with a bounded input set and the simplest matching mode. Fixed strings reduce ambiguity; regular expressions express structure; context and source locations preserve evidence; and exit status makes searches usable in automation.

Next lesson

Finding Files with find, locate, and xargs

The next lesson shifts from matching file content to selecting filesystem objects and acting on them safely.

10. Further reading

  • GNU Grep manual — invocation, matching modes, regular expressions, context, and exit status.
  • GNU Bash Reference Manual — quoting, filename expansion, pipelines, and pipefail.
  • POSIX regular-expression and utility specifications.
  • Linux manual pages for grep, regex, and pcre2pattern where available.
  • GNU Coreutils manual — sort and text-processing composition.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.