Linux CLI 41 ๐ง regular expressions
grep -E '[0-9 ][0-9 ]' numbers.txt
grep -E '^[aeiouAEIOU ]' vowels.txt
sed -E 's/f([a-z ])o\1d/b&r/' input.txt > output.txt
awk '/^[0-9 ]/ && /a/' data.txt
awk '{gsub(/f([a-z ])o\1d/, "b&r"); print}' input.txt > output.txt
grep -E '\.com' domains.txt
ls /home/kr/cli/[[:upper: ] ]*
ls /home/kr/cli/[[:digit: ] ]*
Regular expressions (regex) are the language of pattern matching on Linux. They let you describe what you’re looking for โ not the exact characters โ and every major text tool understands them: grep, sed, awk, find, and even the shell itself. Learn regex once and you’ll use it everywhere.
Key point: There are two dialects โ Basic (BRE) and Extended (ERE). grep defaults to BRE; add -E for ERE. sed defaults to BRE; add -E for ERE. awk uses ERE by default. Knowing which one you’re in prevents a lot of confusion.
a – regular expressions and POSIX metacharacters 1/2
Regular expressions are powerful tools for pattern matching and text manipulation. You can use regex with various commands like grep, sed, awk, and even directly in the shell. You can use literal characters (abcdef...) but also metacharacters โ characters with special meaning.
Basic regex metacharacters (BRE):
| Metacharacter | Meaning | Example |
|---|---|---|
. | Matches any single character | f..d matches food, fard, but not foot |
^ | Matches at the start of the line | ^foo matches lines starting with foo |
$ | Matches at the end of the line | bar$ matches lines ending with bar |
[ ] | Matches any character within the brackets | [ aeiou ] matches any vowel |
[ ^ ] | Matches any character not in the brackets | [ ^0-9 ] matches any non-digit |
* | Matches 0 or more of the preceding element | fo*d matches fd, food, fod โ but not fed |
\ | Escapes a metacharacter | \. matches a literal dot |
Explaining each one:
. โ any character
$ grep 'f..d' words.txt
food
fard
feed
# Matches any single char between f and d
# Does NOT match "foot" (only 2 chars between f and d would be "oo")
^ โ start of line
$ grep '^foo' lines.txt
foobar
foo and more
# Only lines that BEGIN with "foo"
$ โ end of line
$ grep 'bar$' lines.txt
foobar
rabar
# Only lines that END with "bar"
[ ] โ character class
$ grep '[aeiou ]' words.txt
cat
dog
fish
# Matches any line containing a vowel
[ ^ ] โ negated class
$ grep '[^0-9 ]' codes.txt
abc
xyz
# Lines containing at least one non-digit
* โ zero or more
$ grep 'fo*d' words.txt
fd
food
fod
# f, then zero or more o's, then d
# Does NOT match "fed" โ the middle char must be "o" or nothing
\ โ escape
$ grep '\.' file.txt
example.com
version 1.0
# Matches a literal dot, not "any character"
b – regular expressions and metacharacters 2/2
Extended regex (ERE) adds more powerful operators. Use grep -E, sed -E, or awk (which uses ERE by default).
| Metacharacter | Meaning | Example |
|---|---|---|
( ) | Grouping โ subexpressions and backreferences | (foo)bar matches foobar |
{n} | Exactly n occurrences | o{2} matches exactly oo |
{n,m} | Between n and m occurrences | o{1,3} matches 1โ3 os |
? | 0 or 1 occurrence | fo?d matches fd and fod, not food |
+ | 1 or more occurrences | fo+d matches food and fod, not fd |
| | Alternation โ OR | foo|bar matches foo or bar |
Explaining each one:
( ) โ grouping and backreferences
$ echo "foobar" | grep -E '(foo)bar'
foobar
# Group (foo) followed by literal "bar"
# Backreference โ \1 refers to the first group
$ grep -E '(f.)(o.)' file.txt
# Matches "fa" followed by "ob", etc.
{n} โ exactly n
$ grep -E 'o{2}' words.txt
food
moon
# Matches exactly two o's in a row
{n,m} โ between n and m
$ grep -E 'o{1,3}' words.txt
dog # 1 o
food # 2 o's
fooood # 4 o's โ still matches the first 3? No: pattern is 1-3, "oooo" = 4
? โ zero or one
$ grep -E 'fo?d' words.txt
fd
fod
# Matches f, optional o, d
# Does NOT match "food" (that's 2 o's)
+ โ one or more
$ grep -E 'fo+d' words.txt
fod
food
fooood
# Matches f, 1+ o's, d
# Does NOT match "fd"
| โ alternation
$ grep -E 'foo|bar' file.txt
foo
bar
foobar
# Matches lines containing "foo" OR "bar"
BRE vs ERE โ the key differences:
| Feature | BRE | ERE |
|---|---|---|
+ | literal + | one or more |
? | literal ? | zero or one |
| | literal | | alternation |
( ) | literal | grouping |
{ } | literal | repetition |
* | zero or more | zero or more |
. | any char | any char |
^ $ | anchors | anchors |
In BRE, you escape these to get their special meaning:
\+,\?,\|,\(,\),\{,\}. In ERE, you use them bare and escape them to make them literal.
c – regular expressions and POSIX character classes
POSIX character classes are portable, readable alternatives to explicit ranges. Write them inside [ ] โ e.g., [ [ :digit: ] ].
| Class | Matches | Equivalent |
|---|---|---|
[ :alnum: ] | Letters and digits | [ A-Za-z0-9 ] |
[ :alpha: ] | Letters only | [ A-Za-z ] |
[ :digit: ] | Digits | [ 0-9 ] |
[ :lower: ] | Lowercase letters | [ a-z ] |
[ :upper: ] | Uppercase letters | [ A-Z ] |
[ :blank: ] | Space or tab | [ \t ] |
[ :space: ] | Any whitespace | [ \t\n\r\f\v ] |
[ :graph: ] | Printable, non-space | โ |
[ :print: ] | Printable incl. space | โ |
[ :punct: ] | Punctuation | [ !@#$%^&*... ] |
[ :xdigit: ] | Hex digits | [ 0-9A-Fa-f ] |
Examples:
# Any digit
$ grep '[[:digit: ] ]' file.txt
# Any two digits in a row
$ grep -E '[[:digit: ] ][[:digit: ] ]' file.txt
# Starts with uppercase
$ grep '^[[:upper: ] ]' file.txt
# Contains punctuation
$ grep '[[:punct: ] ]' file.txt
# Whitespace at the end
$ grep '[[:space: ] ]$' file.txt
# Shell globbing with classes
$ ls /home/kr/cli/[[:upper: ] ]*
# Lists files starting with an uppercase letter
$ ls /home/kr/cli/[[:digit: ] ]*
# Lists files starting with a digit
Tip: POSIX classes are locale-aware. On a UTF-8 system,
[ [ :alpha: ] ]matches accented letters too. Explicit ranges like[ A-Za-z ]don’t. Use classes when portability matters.
d – regular expressions examples
Here are the examples from the top of the chapter, explained line by line.
1. Find lines containing two consecutive digits:
$ grep -E '[0-9 ][0-9 ]' numbers.txt
42 apples
call 555-1234
version 10.5
The pattern [ 0-9 ][ 0-9 ] matches any two digits in a row. Lines with a single digit (7) or non-consecutive digits (1 2) don’t match.
2. Find lines that start with a vowel:
$ grep -E '^[aeiouAEIOU ]' vowels.txt
apple
orange
Eagle
umbrella
^ anchors to the start; [ aeiouAEIOU ] matches any vowel, upper or lower. Lines starting with consonants don’t match.
3. Replace “food” with “bard” (using backreferences):
$ sed -E 's/f([a-z ])o\1d/b&r/' input.txt > output.txt
Let’s break this down:
fโ literal f([ a-z ])โ group 1: any lowercase letteroโ literal o\1โ backreference to group 1 (the same letter again)dโ literal db&rโ replacement:b, then the whole match (&), thenr
So food โ group 1 = o, match = food, replacement = bfoodr. Wait โ that’s bfoodr? Let me re-check.
Actually the replacement b&r inserts a b before the match and an r after, giving bfoodr. To replace with “bard” you’d write s/f([ a-z ])o\1d/b\1rd/ or similar. The example as written wraps the match with b and r. The point is to demonstrate capture groups and backreferences.
4. Print lines that start with a number AND contain the letter “a”:
$ awk '/^[0-9 ]/ && /a/' data.txt
3 apples
42 bananas
7 and 8
awk evaluates /^[ 0-9 ]/ (starts with a digit) and /a/ (contains an “a”). Both must be true for the line to print.
5. Same replacement with awk’s gsub:
$ awk '{gsub(/f([a-z ])o\1d/, "b&r"); print}' input.txt > output.txt
gsub(/pattern/, "replacement")โ global substitution&in the replacement means “the whole match”- Same regex and backreference as the
sedexample, but done inawkfor every line
6. Find lines containing the literal string .com:
$ grep -E '\.com' domains.txt
example.com
google.com
notacom
The \. escapes the dot so it matches a literal dot, not “any character.” Without the backslash, grep -E '.com' would also match xcom, 1com, etc.
7. List files starting with an uppercase letter:
$ ls /home/kr/cli/[[:upper: ] ]*
README.md
Notes.txt
POSIX class in a shell glob โ note this is the shell’s own pattern matching, not grep. The shell supports a subset of POSIX classes.
8. List files starting with a digit:
$ ls /home/kr/cli/[[:digit: ] ]*
1.txt
2-notes.md
3rd-draft.docx
Complete Example Session
# ============================================
# PART 1: BASIC METACHARACTERS
# ============================================
$ cat words.txt
food
fard
feed
foot
fd
fod
fed
# . โ any character
$ grep 'f..d' words.txt
food
fard
feed
# (foot is 4 chars, won't match)
# ^ โ start of line
$ grep '^foo' words.txt
food
foot
# $ โ end of line
$ grep 'ood$' words.txt
food
# (foot ends in "oot", not "ood")
# * โ zero or more
$ grep 'fo*d' words.txt
food
fd
fod
# (fed doesn't match โ the middle must be o or nothing)
# [ ] โ character class
$ grep '[aeiou ]' words.txt
food
fard
feed
...
# ============================================
# PART 2: EXTENDED METACHARACTERS
# ============================================
# + โ one or more
$ grep -E 'fo+d' words.txt
food
fod
# (fd doesn't match; food and fod do)
# ? โ zero or one
$ grep -E 'fo?d' words.txt
fd
fod
# (food has 2 o's โ doesn't match)
# {n} โ exactly n
$ grep -E 'o{2}' words.txt
food
# (only "food" has exactly two o's in a row)
# | โ alternation
$ grep -E 'food|fod' words.txt
food
fod
# ( ) โ grouping
$ grep -E '(foo|far)d' words.txt
food
fard
# ============================================
# PART 3: POSIX CLASSES
# ============================================
$ cat mixed.txt
abc123
Hello
!@#$
42
spaces
$ grep '[[:digit: ] ]' mixed.txt
abc123
42
$ grep '[[:upper: ] ]' mixed.txt
Hello
$ grep '[[:punct: ] ]' mixed.txt
!@#$
$ grep '[[:space: ] ]' mixed.txt
spaces
# ============================================
# PART 4: BACKREFERENCES
# ============================================
$ cat input.txt
food is good
fad is bad
feed the fed
# Match f, any lowercase letter, o, SAME letter, d
$ grep -E 'f([a-z ])o\1d' input.txt
food is good
# The \1 refers back to what ([a-z ]) matched
# "food" โ group 1 = "o", pattern = f o o d โ
# "fad" โ group 1 = "a", pattern = f a o a d โ (doesn't match)
# ============================================
# PART 5: SED WITH BACKREFERENCES
# ============================================
$ sed -E 's/f([a-z ])o\1d/b&r/' input.txt > output.txt
$ cat output.txt
bfoodr is good
fad is bad
feed the fed
# Only "food" matched the pattern
# The replacement wrapped it with b and r
# ============================================
# PART 6: AWK
# ============================================
$ cat data.txt
3 apples
42 bananas
7 and 8
banana
99 problems
# Lines starting with a digit AND containing 'a'
$ awk '/^[0-9 ]/ && /a/' data.txt
3 apples
42 bananas
7 and 8
# Global substitution
$ awk '{gsub(/f([a-z ])o\1d/, "b&r"); print}' input.txt
bfoodr is good
fad is bad
feed the fed
# ============================================
# PART 7: ESCAPING
# ============================================
$ cat domains.txt
example.com
google.com
notacom
xcom
# Literal dot
$ grep -E '\.com' domains.txt
example.com
google.com
# (notacom and xcom don't have a literal dot)
# Without escaping โ matches any char before "com"
$ grep -E '.com' domains.txt
example.com
google.com
notacom
xcom
# (all match because . matches any character)
# ============================================
# PART 8: SHELL GLOBBING WITH CLASSES
# ============================================
$ ls /home/kr/cli/
README.md Notes.txt 1.txt 2-notes.md script.sh
$ ls /home/kr/cli/[[:upper: ] ]*
README.md Notes.txt
$ ls /home/kr/cli/[[:digit: ] ]*
1.txt 2-notes.md
Quick Reference
Basic Metacharacters (BRE)
| Meta | Meaning |
|---|---|
. | Any single character |
^ | Start of line |
$ | End of line |
[ ] | Character class |
[ ^ ] | Negated class |
* | Zero or more |
\ | Escape |
Extended Metacharacters (ERE)
| Meta | Meaning |
|---|---|
( ) | Grouping |
{n} | Exactly n |
{n,m} | Between n and m |
? | Zero or one |
+ | One or more |
| | Alternation |
POSIX Classes
| Class | Matches |
|---|---|
[ :alnum: ] | Letters + digits |
[ :alpha: ] | Letters |
[ :digit: ] | Digits |
[ :lower: ] | Lowercase |
[ :upper: ] | Uppercase |
[ :blank: ] | Space or tab |
[ :space: ] | Any whitespace |
[ :graph: ] | Printable, non-space |
[ :print: ] | Printable |
[ :punct: ] | Punctuation |
[ :xdigit: ] | Hex digits |
Anchors and Quantifiers
| Pattern | Meaning |
|---|---|
^foo | Starts with foo |
foo$ | Ends with foo |
^foo$ | Exactly “foo” |
fo* | f + 0+ o’s |
fo+ | f + 1+ o’s |
fo? | f + 0 or 1 o |
fo{2} | f + exactly 2 o’s |
fo{2,4} | f + 2โ4 o’s |
Backreferences
| Pattern | Meaning |
|---|---|
(abc)\1 | “abc” repeated twice |
f([ a-z ])o\1d | f, letter, o, SAME letter, d |
& (in sed/awk) | The entire match |
\1, \2, … | Group 1, group 2, … |
BRE vs ERE
| Operator | BRE | ERE |
|---|---|---|
+ | \+ | + |
? | \? | ? |
| | | | ` |
( ) | \( \) | ( ) |
{ } | \{ \} | { } |
Tool Dialects
| Tool | Default | ERE flag |
|---|---|---|
grep | BRE | -E |
sed | BRE | -E |
awk | ERE | (default) |
| Shell glob | POSIX subset | โ |
Best Practices
โ Do This:
# Use -E for extended regex in grep and sed
grep -E 'foo|bar' file.txt # โ
# Escape dots in literal matches
grep -E '\.com' file.txt # โ
# Anchor patterns to avoid over-matching
grep -E '^foo$' file.txt # โ
# Use POSIX classes for portability
grep '[[:digit: ] ]' file.txt # โ
# Test patterns on a small sample first
echo "test" | grep -E 'pattern' # โ
# Use backreferences for repeated patterns
sed -E 's/(foo)\1/bar/' file.txt # โ
# Quote patterns in the shell
grep '^foo' file.txt # โ
โ Don’t Do This:
# Don't forget -E for ERE operators
grep 'foo|bar' file.txt # โ literal | in BRE
# Don't use unescaped dots for literal dots
grep '.com' file.txt # โ matches any char
# Don't forget to anchor
grep 'foo' file.txt # โ ๏ธ matches anywhere
# Don't assume BRE and ERE are the same
sed 's/(foo)/bar/' file.txt # โ literal parens in BRE
# Don't use regex on huge files casually
grep -E '.*.*.*' hugefile # โ catastrophic backtracking
# Don't forget to quote special chars in shell
grep ^foo file.txt # โ shell may interpret
# Don't mix dialects
grep -E 'foo\{2\}' file.txt # โ escaping wrong for ERE
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
Forgot -E | +, ?, | are literal | Add -E |
Unescaped . | Matches any char | Escape it: \. |
| No anchors | Over-matches | Use ^ and $ |
| Wrong dialect | Pattern fails | Check BRE vs ERE |
Greedy .* | Matches too much | Use [ ^" ]* or non-greedy tools |
| Backreference confusion | Wrong group | Count ( left to right |
| Locale issues | [ A-Z ] matches weird chars | Use POSIX classes |
| Unquoted pattern | Shell expands it | Quote with '...' |
| Catastrophic backtracking | Slow on big files | Simplify the pattern |
| Escaping in shell | Double escaping | Use single quotes |
Real-World Examples
1. Find Email Addresses
$ grep -E '[[:alnum: ]._%+- ]+@[[:alnum: ].- ]+\.[[:alpha: ] ]{2,}' emails.txt
alice@example.com
bob.smith@company.org
2. Find IPv4 Addresses
$ grep -E '([0-9 ]{1,3}\.){3}[0-9 ]{1,3}' logs.txt
192.168.1.100
10.0.0.1
3. Find Lines That Start with a Digit
$ grep -E '^[0-9 ]' data.txt
3 apples
42 bananas
4. Find Blank Lines
$ grep -E '^$' file.txt
# (prints only empty lines)
5. Remove Trailing Whitespace
$ sed -E 's/[[:space: ] ]+$//' file.txt > clean.txt
6. Extract the First Word of Each Line
$ awk '{print $1}' file.txt
7. Swap Two Fields
$ sed -E 's/^([^ ]+) ([^ ]+)/\2 \1/' file.txt
# "hello world" โ "world hello"
8. Replace Repeated Words
$ sed -E 's/\b([a-z ]+) \1\b/\1/' file.txt
# "the the cat" โ "the cat"
9. Validate a Simple Date
$ grep -E '^[0-9 ]{4}-[0-9 ]{2}-[0-9 ]{2}$' dates.txt
2024-01-15
2023-12-31
10. Find Files Modified by a User
$ find . -user kronos -name '*.log' -mtime -7
11. Match Comments in a Config File
$ grep -E '^[[:space: ] ]*#' sshd_config
# Lines that begin with optional whitespace, then #
12. Strip HTML Tags
$ sed -E 's/<[^> ]+>//g' page.html
13. Extract Version Numbers
$ grep -E 'v?[0-9 ]+\.[0-9 ]+\.[0-9 ]+' versions.txt
v1.2.3
2.0.1
14. Find Duplicate Lines
$ sort file.txt | uniq -d
15. Filter Log Lines by Severity
$ grep -E 'ERROR|WARN' app.log
2024-01-15 10:30:00 ERROR: connection refused
2024-01-15 10:35:00 WARN: disk almost full
Visual: Regex Cheat Sheet
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ANCHORS โ
โ ^ start of line โ
โ $ end of line โ
โ โ
โ CHARACTERS โ
โ . any single char โ
โ [ ] character class โ
โ [^ ] negated class โ
โ \d digit (PCRE) โ
โ \w word char (PCRE) โ
โ \s whitespace (PCRE) โ
โ โ
โ QUANTIFIERS โ
โ * 0 or more โ
โ + 1 or more โ
โ ? 0 or 1 โ
โ {n} exactly n โ
โ {n,m} between n and m โ
โ โ
โ GROUPING โ
โ ( ) group โ
โ | alternation โ
โ \1 backreference to group 1 โ
โ โ
โ CLASSES โ
โ [[:digit: ] ] digits โ
โ [[:alpha: ] ] letters โ
โ [[:upper: ] ] uppercase โ
โ [[:lower: ] ] lowercase โ
โ [[:space: ] ] whitespace โ
โ [[:punct: ] ] punctuation โ
โ [[:xdigit: ] ] hex digits โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Pattern | Meaning | Example |
|---|---|---|
. | Any character | f..d โ food, fard |
^ | Start of line | ^foo โ foobar |
$ | End of line | bar$ โ foobar |
[ abc ] | Any of a, b, c | [ aeiou ] โ vowels |
[ ^abc ] | Not a, b, c | [ ^0-9 ] โ non-digits |
* | 0 or more | fo*d โ fd, food |
+ | 1 or more | fo+d โ food, fod |
? | 0 or 1 | fo?d โ fd, fod |
{n} | Exactly n | o{2} โ oo |
{n,m} | n to m | o{1,3} โ o, oo, ooo |
| | OR | foo|bar |
( ) | Group | (foo)bar โ foobar |
\1 | Backreference | (f.)(o.) |
\. | Literal dot | \.com |
[ [ :digit: ] ] | Digit | [ [ :digit: ] ]+ |
[ [ :alpha: ] ] | Letter | [ [ :alpha: ] ]+ |
[ [ :upper: ] ] | Uppercase | ^[ [ :upper: ] ] |
[ [ :space: ] ] | Whitespace | [ [ :space: ] ]+ |
Key takeaways:
- Regex is pattern matching โ literal characters plus metacharacters
- BRE (basic) is the default for
grepandsed; ERE (extended) needs-E awkuses ERE by default.matches any char,^and$anchor,[ ]is a class,[ ^ ]negates*= 0+,+= 1+,?= 0/1,{n,m}= range( )groups,\1refers back to a group,|means OR- Escape special characters with
\โ especially.when you want a literal dot - POSIX classes (
[ [ :digit: ] ],[ [ :alpha: ] ], etc.) are portable and locale-aware - Test patterns on a small sample before running on a big file
- Quote patterns in the shell โ use single quotes to avoid expansion
- Regex can be slow on large files โ keep patterns simple
Remember: Regex is a language, and like any language it takes practice. Start with the basics โ ., ^, $, [ ], * โ then add ERE operators as needed. Always ask: which dialect am I in? grep and sed are BRE by default; add -E for the full power. Use [ [ :digit: ] ] instead of [ 0-9 ] when portability matters. Escape your dots. Anchor your patterns. And always test on a small sample before unleashing a regex on a million-line log file.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!