Name Calling with PowerShell and Regular Expressions
In this issue:
We've been looking at some of the more advanced features of using regular expressions in PowerShell. I will admit to being overwhelmed with regular expressions when I first started working with them. I thought that I would never be able to understand them and it was easy to let myself become overwhelmed.
But over time, and with practice, I realized this was just another type of language to learn and that the more I used it, the easier it would become. Or course, it helps if you have a reason to learn and use regular expressions. Maybe today's content will pique your interest.
Optional Match
We know that the whole point of a regular expression is to describe a string of text programmatically. We aren't testing necessarily that the string is a valid email address, but it at least looks like one. The syntax for defining a pattern can be daunting. We have been looking at explicit matching patterns. However, sometimes you need to account for something that might exist.
Let's say I have a list of possible server names. My naming convention is 3 characters followed by one or more numbers.
PS C:\> "SRV1","FPS22","BADSRV21","FPSS","DOM5","SRV-A7","APP-B22" | where {$_ -match "[a-zA-Z]{3}\d+"}
SRV1
FPS22
BADSRV21
DOM5
Using Anchors
My pattern is searching for a string that has exactly 3 characters between a-z and A-Z followed by one or more digits. This is close and I chose this example to remind you about the float. The string BADSRV21 also matched the pattern because the pattern matched somewhere in the string. Sometimes you may want to this behavior. That's OK. But I should really refine this using anchors.
PS C:\> "SRV1","FPS22","BADSRV21","FPSS","DOM5","SRV-A7","APP-B22" | where {$_ -match "^[a-zA-Z]{3}\d+$"}
SRV1
FPS22
DOM5
I am now using the starting anchor (^) and the closing anchor ($). This is better.
However, some server names like SRV-A7 are also valid. It is possible the name will have a dash, followed by a letter and then 1 or more numbers.
Let me test a pattern on that alone.
PS C:\> "SRV1","FPS22","BADSRV21","FPSS","DOM5","SRV-A7","APP-B22" | where {$_ -match "\-[a-zA-Z]{1}\d+"}
SRV-A7
APP-B22
Now I need to combine patterns. This pattern is optional. The syntax for an optional match is (<pattern>)?. I always use the parentheses.
PS C:\> "abcd" -match "ab(c)?d"
True
PS C:\> $matches
Name Value
---- -----
1 c
0 abcd
PS C:\> "abd" -match "ab(c)?d"
True
PS C:\> $matches
Name Value
---- -----
0 abd
I want to match on a string of abcd or abd. The c is optional. This should also fail when expected.
PS C:\> "abxd" -match "ab(c)?d"
False
With this syntax in mind, I can revise my server name pattern.
PS C:\> "SRV1","FPS22","BADSRV21","FPSS","DOM5","SRV-A7","APP-B22" | where {$_ -match "^[a-zA-Z]{3}(\-[a-zA-Z]{1})?\d+"}
SRV1
FPS22
DOM5
SRV-A7
APP-B22
Let's look at another example.
I want to find all .ps1, .psd1 and .psm1 files in a folder. There is a pattern. The letters d and m are optional.
PS C:\> dir c:\work\ -file | where name -match "\.ps([dm])?1$"
Directory: C:\work
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 8/20/2024 10:41 AM 1100 dev-getTerminalGui.ps1
-a--- 5/28/2024 11:25 AM 1841 dev-reminders.ps1
-a--- 12/4/2024 9:19 AM 10402 ExportToPDF.ps1
-a--- 3/29/2023 10:34 AM 6794 Get-AboutOnline.ps1
-a--- 2/4/2026 4:55 PM 839 Get-PwshRegistryInfo.ps1
-a--- 10/31/2024 2:36 PM 92 GetReleaseNote.ps1
-a--- 7/18/2025 11:35 AM 7686 m.psd1
-a--- 9/19/2024 3:14 PM 1431 New-ReleaseChangeLog.ps1
-a--- 6/16/2025 9:22 AM 468 NewHelpPDF.ps1
-a--- 11/7/2025 1:19 PM 1077 PrepNewRelease.ps1
-a--- 3/11/2026 3:24 PM 2886 PSWorkItem.psd1
-a--- 3/11/2026 3:26 PM 7193 PSWorkItem.psm1
The pattern is anchored at the end. I am also testing for a literal period using \.