Chapter Objective: Construct regular expressions using anchors, character classes, and quantifiers, and apply them practically with grep and sed to search and transform text.

Key commands: grep -E, grep -v, sed 's/.../.../'

What Is a Regular Expression?

A regular expression (regex) is a pattern that describes a set of matching text, rather than one exact string. Regular expressions power search and substitution across many Linux tools — grep, sed, awk, and more all understand them.

FlavorUsed ByNotes
BRE (Basic Regular Expressions)grep, sed by defaultSpecial characters like + and ? need a backslash to act as metacharacters
ERE (Extended Regular Expressions)grep -E, sed -E, awkMore metacharacters work without escaping — generally easier to read and write
🔵 Why It Matters A regex is not a filename wildcard, even though the syntax can look superficially similar. * in a wildcard means "any characters"; * in a regex means "zero or more of the previous character." Mixing the two mental models up is one of the most common sources of confusion for new administrators.

Anchors

AnchorMatches
^The start of a line
$The end of a line
# Lines that START with "root"
grep '^root' /etc/passwd

# Lines that END with "bash"
grep 'bash$' /etc/passwd

# Match an entirely empty line
grep '^$' file.txt
✅ Tip — Anchors Narrow Results Fast Searching /etc/passwd for root without an anchor also matches usernames or comments that merely contain "root" somewhere. ^root narrows it down to lines that actually start with it.

Character Classes

PatternMatches
.Any single character
[abc]Any one character in the set: a, b, or c
[a-z]Any one lowercase letter
[^abc]Any one character NOT in the set
[[:digit:]]Any one digit (POSIX class)
[[:alpha:]]Any one letter (POSIX class)
[[:space:]]Any one whitespace character (POSIX class)
# Lines containing any digit
grep '[[:digit:]]' file.txt

# Lines that do NOT start with a letter
grep '^[^[:alpha:]]' file.txt

# . matches ANY character — including a literal dot
grep 'r.ot' file.txt   # matches "root", "r3ot", "r.ot", etc.
⚠️ Warning — . Isn't a Literal Dot To match an actual period character, escape it: \.. An unescaped . in a regex matches any single character, which surprises a lot of people searching for something like an IP address or filename extension.

Quantifiers

QuantifierMeaning
*Zero or more of the preceding character/group
+ (ERE)One or more of the preceding character/group
? (ERE)Zero or one of the preceding character/group
{n}Exactly n occurrences
{n,m}Between n and m occurrences
# One or more digits (extended regex, so -E is needed for +)
grep -E '[[:digit:]]+' file.txt

# A word that optionally ends in "s"
grep -E 'cats?' file.txt

# Exactly 3 digits, useful for a rough IP-octet check
grep -E '[[:digit:]]{1,3}' file.txt
🔵 Exam Note In basic regex (plain grep), + and ? only work as metacharacters when escaped (\+, \?). Switching to grep -E is usually simpler than remembering which characters need escaping in BRE.

Grouping and Alternation

# Match either "cat" or "dog" (extended regex)
grep -E 'cat|dog' file.txt

# Group part of a pattern so a quantifier applies to the whole group
grep -E '(ab)+' file.txt   # matches "ab", "abab", "ababab"...

# Combine grouping and alternation
grep -E '(http|https)://' file.txt
✅ Tip — Parentheses Change What a Quantifier Applies To ab+ means "a, followed by one or more b" — the + only applies to the b. (ab)+ means "one or more repetitions of ab" — the parentheses group the whole unit.

Practical grep

# Case-insensitive search
grep -i error /var/log/messages

# Show line numbers alongside matches
grep -n error /var/log/messages

# Invert the match — show lines that DON'T match
grep -v '^#' /etc/ssh/sshd_config

# Recursively search every file under a directory
grep -r 'TODO' /home/student/project/

# Extended regex, combined with other flags
grep -Ei '(warning|error|fail)' /var/log/messages
✅ Tip — grep -v Is a Filter, Not Just an Inverter grep -v '^#' against a config file is a fast way to see only the active (non-comment) settings — filtering out clutter is often as useful as finding a match.

Practical sed Substitution

sed (stream editor) applies regular expressions to transform text, most commonly through substitution.

# Replace the first occurrence on each line
sed 's/old/new/' file.txt

# Replace ALL occurrences on each line
sed 's/old/new/g' file.txt

# Edit the file directly, in place
sed -i 's/old/new/g' file.txt

# Use extended regex syntax with sed too
sed -E 's/(http|https):/URL:/g' file.txt

# Delete lines matching a pattern
sed '/^#/d' file.txt
⚠️ Warning — -i Overwrites Without Asking sed -i edits the file in place with no confirmation and no automatic backup. Test your pattern first without -i (so it only prints to the screen), or use sed -i.bak to keep a backup copy before editing a file you can't afford to lose.

Key Terms for Chapter 2

regular expression (regex)
A pattern describing a set of matching text
BRE / ERE
Basic and Extended Regular Expression syntax; ERE supports more metacharacters unescaped
anchor
A pattern element matching a position rather than a character, such as ^ or $
character class
A pattern element matching any one character from a defined set, e.g. [a-z]
quantifier
A pattern element controlling how many times the preceding element may repeat
alternation
Matching one of several alternatives, using |
sed
Stream editor; applies regex-based transformations like substitution to text

Review Questions

  1. What is the difference between a shell wildcard's * and a regex's *?
  2. Write a grep pattern that matches lines starting with "root".
  3. What does an unescaped . actually match in a regular expression?
  4. Why would you need grep -E to use + as "one or more," rather than plain grep?
  5. What is the difference between the patterns ab+ and (ab)+?
  6. Write a grep command that shows every non-comment line in /etc/ssh/sshd_config.
  7. Why is it good practice to test a sed substitution without -i first?