|

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):

MetacharacterMeaningExample
.Matches any single characterf..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 linebar$ 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 elementfo*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).

MetacharacterMeaningExample
( )Grouping โ€” subexpressions and backreferences(foo)bar matches foobar
{n}Exactly n occurrenceso{2} matches exactly oo
{n,m}Between n and m occurrenceso{1,3} matches 1โ€“3 os
?0 or 1 occurrencefo?d matches fd and fod, not food
+1 or more occurrencesfo+d matches food and fod, not fd
|Alternation โ€” ORfoo|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:

FeatureBREERE
+literal +one or more
?literal ?zero or one
|literal |alternation
( )literalgrouping
{ }literalrepetition
*zero or morezero or more
.any charany char
^ $anchorsanchors

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: ] ].

ClassMatchesEquivalent
[ :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 letter
  • o โ€” literal o
  • \1 โ€” backreference to group 1 (the same letter again)
  • d โ€” literal d
  • b&r โ€” replacement: b, then the whole match (&), then r

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 sed example, but done in awk for 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)

MetaMeaning
.Any single character
^Start of line
$End of line
[ ]Character class
[ ^ ]Negated class
*Zero or more
\Escape

Extended Metacharacters (ERE)

MetaMeaning
( )Grouping
{n}Exactly n
{n,m}Between n and m
?Zero or one
+One or more
|Alternation

POSIX Classes

ClassMatches
[ :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

PatternMeaning
^fooStarts 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

PatternMeaning
(abc)\1“abc” repeated twice
f([ a-z ])o\1df, letter, o, SAME letter, d
& (in sed/awk)The entire match
\1, \2, …Group 1, group 2, …

BRE vs ERE

OperatorBREERE
+\++
?\??
||`
( )\( \)( )
{ }\{ \}{ }

Tool Dialects

ToolDefaultERE flag
grepBRE-E
sedBRE-E
awkERE(default)
Shell globPOSIX 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

PitfallProblemSolution
Forgot -E+, ?, | are literalAdd -E
Unescaped .Matches any charEscape it: \.
No anchorsOver-matchesUse ^ and $
Wrong dialectPattern failsCheck BRE vs ERE
Greedy .*Matches too muchUse [ ^" ]* or non-greedy tools
Backreference confusionWrong groupCount ( left to right
Locale issues[ A-Z ] matches weird charsUse POSIX classes
Unquoted patternShell expands itQuote with '...'
Catastrophic backtrackingSlow on big filesSimplify the pattern
Escaping in shellDouble escapingUse 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

PatternMeaningExample
.Any characterf..d โ†’ food, fard
^Start of line^foo โ†’ foobar
$End of linebar$ โ†’ foobar
[ abc ]Any of a, b, c[ aeiou ] โ†’ vowels
[ ^abc ]Not a, b, c[ ^0-9 ] โ†’ non-digits
*0 or morefo*d โ†’ fd, food
+1 or morefo+d โ†’ food, fod
?0 or 1fo?d โ†’ fd, fod
{n}Exactly no{2} โ†’ oo
{n,m}n to mo{1,3} โ†’ o, oo, ooo
|ORfoo|bar
( )Group(foo)bar โ†’ foobar
\1Backreference(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 grep and sed; ERE (extended) needs -E
  • awk uses ERE by default
  • . matches any char, ^ and $ anchor, [ ] is a class, [ ^ ] negates
  • * = 0+, + = 1+, ? = 0/1, {n,m} = range
  • ( ) groups, \1 refers 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!