Getting Started With PowerShell Regular Expressions
In this issue:
- Like vs Match
- Why Do Regular Expressions Matter?
- Word Characters
- Numeric Characters
- Matching White Space
- Quantifiers
- Summary
Over the years of writing this newsletter, I've share a lot of code, often using regular expressions. If you go back through the Archive you'll find I have written about some advanced regular expression techniques. But I don't think I'v ever provide an introduction to using regular expressions in PowerShell.
I understand that this can appear to be a daunting topic. When I was first introduced it all felt so cryptic and complicated. I thought, "I'll never be able to learn this." Howe,ver learning to use regular expressions, often referred to as regex, is like learning a foreign language. The process is no different than what you went through to learn PowerShell. I'm sure PowerShell felt cryptic and complicated at first.
But stick with it. Once you understand how to use regular expressions in PowerShell, and features that take advantage of it, you'll be glad you did. Using regular expressions can improve the efficiency of your code and mark you as an experienced PowerShell user and scripter.
Over the course of the next several newsletters, I want to give an introduction to regular expressions and help take away some of the mystery. While you can certainly use AI tools to create and explain regular expression patterns, you'll get more out of that process if you have experience and some degree of regex literacy.
Like vs Match
Let me begin with the two primary operators you'll use in PowerShell to compare a string to a pattern. Both operators will provide a Boolean result of the comparison. This means you can use them in If statements and Where-Object filtering.
-Like
The -Like operator is not quite a regular expression operator. It is designed to broadly compare strings using the wildcard character, *.
PS C:\> "PowerShell" -Like "*shell"
True
I get a True result because the string shell is at the end of the string. The comparison is case-insensitive by default. If you need a case-sensitive comparison, use the -cLike operator.
PS C:\> "PowerShell" -cLike "P*shell"
False
PS C:\> "PowerShell" -cLike "P*Shell"
True
> *Technically, there is a -iLike operator that performs a case-insensitive match. Which means it works the same as -Like. There's no reason to use it unless you want to be explicit about making a case-insensitive match.
This wildcard comparison is often used in PowerShell commands.
PS C:\> Get-Process -name pw*
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
163 165.68 317.27 129.95 16844 2 pwsh
179 126.95 281.99 54.19 17296 2 pwsh
This is the recommended approach as opposed to filtering with Where-Object.
PS C:\> Get-Process | Where name -like pw*
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
163 165.75 317.37 130.88 16844 2 pwsh
181 126.80 281.86 54.73 17296 2 pwsh
-NotLike
That said, you might use the -NotLike operator to find negative matches. This kind of operation typically involves Where-Object.
PS C:\> Get-Process p* | Where Name -notLike pwsh
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
295 385.72 576.20 1,315.33 23600 2 PhoneExperienceHost
25 10.07 34.55 40.30 7024 0 pia-service
33 52.31 10.22 39.67 5040 2 PowerMgr
47 39.49 44.82 20.98 17424 2 PowerToys
61 35.17 13.50 1.70 12364 2 PowerToys.AdvancedPaste
12 19.27 17.94 35.38 14804 2 PowerToys.AlwaysOnTop
43 12.37 19.27 12.52 17780 2 PowerToys.Awake
11 19.06 2.78 0.64 18656 2 PowerToys.CropAndLock
16 56.38 5.93 5.61 33652 2 PowerToys.FancyZones
9 2.22 4.12 4.36 18184 2 PowerToys.LightSwitchService
53 33.98 13.99 1.36 30784 2 PowerToys.Peek.UI
121 270.18 145.22 62.17 28172 2 PowerToys.PowerLauncher
78 59.13 47.23 2.94 10392 2 PowerToys.QuickAccess
I'm doing an initial filter on processes that start with p then filtering out those with a name of pwsh. I'm using an exact match and not using a wildcard.
-Match
To create a more granular comparison and to take advantage of regular expressions, you will use the -Match operator.
PS C:\> "my name is jeff" -match "jeff"
True
PS C:\> "my name is Jeff" -match "jeff"
True
PS C:\> "my name is Jeff" -match "jason"
False
The operator compares the pattern on the right-side of the operator to the string on the left-side. As you can see it is not case-sensitive by default.
> I won't demo it, but you can use -cMatch to force case-insensitive match. You also have iMatch.
In this example, technically I am using regular expression patterns of jeff and jason.
There is one more bit of PowerShell magic that happens when you use -Match. PowerShell will automatically populate a built-in variable called $matches with the comparison result.
PS C:\> "my name is Jeff" -match "jeff"
True
PS C:\> $matches
Name Value
---- -----
0 Jeff
The object shows the value in the string that matched the pattern. I'll be using this a lot in my examples.
-NotMatch
Likewise, you can use -NotMatch to test if the string doesn't match a given pattern.
PS C:\> "my name is Jeff" -NotMatch "jason"
True
Because this is a negative comparison, $matches isn't updated.
Why Do Regular Expressions Matter?
Before I dive into regular expression patterns, let's take a moment to define what we're working with. A regular expression is a programmatic way of explaining or identifying a string based on a known pattern. We do pattern recognition all the time. In the United States a number like 123-44-5678 looks like a Social Security number. 888-9876 might be a US phone number. The string rgbiv@colorama.org might be an email address. We recognize the pattern. A regular expression is way of describing that pattern in code. It is important to understand that a regex pattern only tells you if the string is a pattern match. It doesn't tell you if it is valid. I can write code to prove that \\SRV1\Files looks like a UNC path. But I will need to write separate code to validate it if required.
Let's get to the basics.