Learn PowerShell - Regular Expressions
Episode 11 of 31

Learn PowerShell - Regular Expressions

Filtering and extracting text with regular expressions: basic patterns with metacharacters and quantifiers, the -match and -replace operators, searching files with Select-String, static methods of the regex class, common patterns for email, IP, date, and phone number, plus best practices for fast and readable regex.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 10 you worked with structured data: CSV, JSON, and XML — all neatly organized. But the real world is messier: server logs, output from legacy programs, and user input are free text without structure. To filter, validate, and extract from such text, you need regular expressions — patterns that recognize text.

Think of regex as a metal detector: it doesn't look for exact text, but for patterns — "find everything that looks like an IP address", "find all invoice numbers". You touched the concept via the -match operator in episode 4; episode 11 covers it thoroughly.

Basic Metacharacters

Regex is built from literals (exact text) and metacharacters (symbols with special meanings):

SymbolMeaningExample
.Any single charactera.c matches abc, axc
*Zero or more of the previousab*c matches ac, abc, abbc
+One or more of the previousab+c matches abc, abbc
?Zero or one of the previouscolou?r matches color, colour
^Start of string^ERROR matches strings starting with ERROR
$End of stringdone$ matches strings ending with done

In PowerShell, the -match and -replace operators use regex. Note the importance of the ^ and $ anchors — without them, -match "ERROR" also matches "NOT_AN_ERROR" because the pattern is found in the middle:

Basic -match
"NOT_AN_ERROR" -match "ERROR"
"NOT_AN_ERROR" -match "^ERROR"
"abc" -match "a.c"
"axc" -match "a.c"

Character Classes

The character class [...] matches one character from the set:

Character classes
"a1" -match "^[a-z][0-9]$"
"Z9" -match "^[A-Z][0-9]$"
"abc" -match "^[a-c]+$"

[a-z] is the lowercase range, [0-9] the number range, [A-Z] uppercase. Combinations like [a-zA-Z0-9] cover letters and numbers. There are also common shorthand classes: \d digits, \w letters-or-numbers-or-underscore, \s whitespace.

Shorthand classes
"Tahun 2026" -match "\d{4}"

Quantifiers

Quantifiers specify the number of repetitions:

QuantifierMeaning
*0 or more
+1 or more
?0 or 1
{n}exactly n times
{n,}n times or more
{n,m}between n and m times
Quantifiers
"1234" -match "^\d{4}$"
"2026-08-03" -match "^\d{2,4}-\d{1,2}-\d{1,2}$"
"a" -match "^a{1,3}$"
"aaa" -match "^a{1,3}$"

{n,m} limits the range — the date 08-03 matches \d{1,2} because one or two digits are both accepted.

The -match, -notmatch, -replace Operators

Three main operators for using regex:

  • -match — returns true or false and populates $matches.
  • -notmatch — its opposite.
  • -replace — replaces matching parts with new text.
-replace
"web-01" -replace "^web-", "app-"
"invoice-2026.pdf" -replace "\d{4}", "2027"
"2026-08-03" -replace "-", "/"

-replace can use patterns and replacement strings that include capture groups — for example swapping date formats:

-replace with groups
"2026-08-03" -replace "^(\d{4})-(\d{2})-(\d{2})$", "$3/$2/$1"

$matches and Capture Groups

When -match succeeds, the matching result is stored in the automatic variable $matches — a dictionary: the whole match in key 0, each capture group (parts inside parentheses) in keys 1, 2, and so on:

Capture groups and $matches
if ("server-42" -match "^(server)-(\d+)$") {
    $matches[1]
    $matches[2]
}

Capture groups make extraction precise: not just "does it match?", but "which part matches?" — the $matches[1] and $matches[2] pair is data you can use directly.

Named Capture Groups

Naming groups makes code more readable than remembering numbers:

Named capture group
if ("web-01:8080" -match "^(?<host>[^:]+):(?<port>\d+)$") {
    $matches.host
    $matches.port
}

With the (?<name>...) syntax, dictionary keys are named — $matches.host and $matches.port read like object properties.

Select-String: Searching Files

Select-String searches for text inside files — like grep in the terminal:

Basic Select-String
Select-String -Path /var/log/syslog -Pattern "ERROR"
Get-ChildItem /var/log -File | Select-String -Pattern "ERROR"

Search configurations you'll use often:

  • -Context — shows lines before and after a match.
  • -CaseSensitive — case-sensitive matching (default: not case-sensitive).
  • Multiple patterns at once via an array.
Context and multiple patterns
Select-String -Path /var/log/syslog -Pattern "ERROR", "WARN" -Context 1, 2

Select-String output is MatchInfo objects with Line, LineNumber, Filename, and Pattern properties — data ready for pipeline processing:

Processing Select-String results
Select-String -Path /var/log/syslog -Pattern "ERROR" |
    Select-Object Filename, LineNumber, Line

The [regex] Class: Match, Matches, Replace, Split

For full control, use the .NET [regex] class with its static methods:

[regex] static methods
[regex]::Match("2026-08-03", "\d+").Value
[regex]::Matches("a1 b22 c333", "\d+") | ForEach-Object { $_.Value }
[regex]::Replace("2026-08-03", "-", "/")
[regex]::Split("a,b,c", ",")
MethodFunction
MatchFirst match, a Match object
MatchesAll matches, a loopable collection
ReplaceReplace all matches
SplitSplit a string based on a pattern

[regex]::Matches returns a collection — each element has Value, Index, and Groups. Useful when you need all matches at once, not just the first.

Common Patterns You'll Use

Patterns you'll almost certainly need in real scripts:

Common patterns
$email   = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
$ipv4    = "^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$"
$tanggal = "^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$"
$hp      = "^\+?62[0-9]{8,13}$"
 
"user@contoh.com" -match $email
"192.168.1.10" -match $ipv4
"2026-08-03" -match $tanggal
"+6281234567890" -match $hp

The IP pattern above looks complicated, but its logic is organized: each octet must be 0-255, expressed via alternatives 25[0-5] (250-255) or 2[0-4]\d (200-249) or others. Store patterns like these as named constants at the top of your scripts — with names, the intent is clear without parsing character by character.

Warning

Validating patterns in production needs extra care: the email/IP patterns above are sufficient for most cases, but not perfect for every situation. Real examples: -match is case-insensitive by default and doesn't require a full match without the ^ and $ anchors. Always test against a real data set before relying on it to block input.

Regex Best Practices

Tips that save you from regex nightmares:

  • Always anchor — use ^ and $ when you want a full match; without them, -match matches wherever the pattern appears.
  • Escape literals — if you want punctuation as-is, escape with a backslash: \. matches a dot, \* matches a star.
  • Greedy can mislead — the * and + quantifiers are greedy (taking as much as possible). Use *? or constrain with anchors when needed.
  • Named groups are more readable(?<name>...) is clearer than remembering group numbers.
  • Test before use — run the pattern on a small sample of data; then scale up.
  • Regex isn't the only tool — for exact string searches, -like or .Contains is much faster.
Testing a pattern with sample data
$pola = "^[a-z0-9_-]+$"
@("nama_valid", "NAMA SALAH!", "user-1") | ForEach-Object {
    "$_ -> $($_ -match $pola)"
}

Conclusion

Episode 11 completes your text-filtering arsenal: the basic metacharacters . * + ? ^ $; character classes and shorthands like \d; quantifiers with ranges; the -match, -notmatch, and -replace operators; $matches along with capture groups and named groups; file searching with Select-String; static methods of the [regex] class; common patterns for email, IP, date, and phone numbers; and best practices for regex that's fast and painless.

Key takeaways:

  • -match doesn't require a full match — use ^ and $.
  • $matches[1] takes a capture group; (?<name>...) names it.
  • Select-String is PowerShell's grep with object output.
  • [regex]::Matches gives all matches, not just the first.
  • Test patterns against real data before using them to block or validate.

The regular expressions material closes the foundation section: conditionals, loops, functions, pipeline, files, structured data, and text patterns — you can now read and write real scripts. But real scripts will inevitably face failures. In episode 12 we learn error handling — turning failure from a mysterious enemy into a controlled event. See you in episode 12!

Learn PowerShell - Regular Expressions | Learn PowerShell