Let's say you have a scenario where (unfortunately) you've got to dive into regular expressions (regex). I stay clear of regex as much as I can but there are times when it really does come in handy if you can deal with the nuances. In today's article, I'd like to go over how I used regex groups to convert ugly text into a nice, structured PowerShell object.

In my day job, we have a standard naming convention for server names. They all have a convention that goes like this:

<EnvironmentId><ServiceID><ServiceInstance><ApplicationId>

This forms server names like BAPP07GEN12 and BSQL10RAS10 and so on. The environment ID is always a single letter, the service ID and application IDs are always three letters and the service instance is always two digits. They follow a structure but the name itself is still just a string. I needed a way to essentially convert that string to an object that looked like this:

[pscustomobject]@{
    EnvironmentId = 'B'
    ServiceId = 'APP'
    ServiceInstance = 7
    ApplicationId = 'GEN'
}

Here's how I did it using named regex groups.

First, I'm going to depend on the automatic variable $matches. This is a variable that PowerShell provides that contains the latest strings that have been matched by using the -match operator. Because this variable is always around and could be populated with something before I start, I'll first ensure it's nulled out.

$matches = $null

Next, I'll create my regex string. I use regex grouping a lot because it's an easy way to pull out text from strings. �Regex groups allow you to enclose a certain portion of a string in parentheses and then call on what the match was later. For example, typical regex groups might look something like this:

$ComputerName = 'BAPP07GEN12'
$ComputerName -match '(\w{1})(\w{3})(\d{2})(\w{3})'

If you're not familiar with regex, the \w represents a single letter and the \d represents a single digit. The curly braces that are enclosing a number right after each of those represents repetition or how many times that is supposed to repeat. In our example above, I'm looking for a single letter at the far left of the string, followed by three more letters, followed by two digits and finally three more letters.

I'm enclosing each of these in parentheses so the $matches variable will contain what was actually matched when it's done.

This is what $matches would look like afterwards.

Name    Value 
----    ----- 
4       GEN
3       07
2       APP
1       B
0       BAPP07GEN

I can now pull out each of the pieces from the computer name as properties with $matches[0], $matches[1] and so on. That's nice but I don't know which of the numbers in the Name column match up to my real-world circumstance of EnvironmentId, ServiceId, ApplicationId and ServiceInstance. It'd be nice to have those labels directly in the $matches variable rather than having to do some further processing.

Introducing Named Groups

If you've already got the hang of regex group, taking the next step to named group is easy. It's just a matter of applying "labels" to each of the groups. This is done by adding ?<Label> inside of the parentheses in each group. By doing so, $matches won't have a simple number for the Name but will have actual labels.

Let's take our example from above and use named groups instead.

$ComputerName = 'BAPP07GEN12'
$ComputerName -match '(?\w{1})(?\w{3})(?\d{2})(?\w{3})'

$matches now looks like this:

Name                 Value
----                 ----- 
ServiceInstance      07 
EnvironmentId        B
ApplicationId        GEN
ServiceId            APP
0                    BAPP07GEN

I now know which group in the $ComputerName string correlates to my object properties. Great, but this is still in the $matches variable. I need to create a custom object from this. Since I now know the property names and values, creating a custom object is as simple as matching them up.

[pscustomobject]@{
    'EnvironmentId' = $matches.EnvironmentId
    'ServiceId' = $matches.ServiceId
    'ApplicationId' = $matches.ApplicationId
    'ServiceInstance' = [int]$matches.ServiceInstance
}

EnvironmentId ServiceId  ApplicationId    ServiceInstance
------------- ---------  -------------    --------------- 
B             APP        GEN                            7 

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!