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.
| Flavor | Used By | Notes |
|---|---|---|
| BRE (Basic Regular Expressions) | grep, sed by default | Special characters like + and ? need a backslash to act as metacharacters |
| ERE (Extended Regular Expressions) | grep -E, sed -E, awk | More 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
| Anchor | Matches |
|---|---|
^ | 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
| Pattern | Matches |
|---|---|
. | 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
| Quantifier | Meaning |
|---|---|
* | 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
- What is the difference between a shell wildcard's
*and a regex's*? - Write a
greppattern that matches lines starting with "root". - What does an unescaped
.actually match in a regular expression? - Why would you need
grep -Eto use+as "one or more," rather than plaingrep? - What is the difference between the patterns
ab+and(ab)+? - Write a
grepcommand that shows every non-comment line in/etc/ssh/sshd_config. - Why is it good practice to test a
sedsubstitution without-ifirst?