Skip to content

Instantly share code, notes, and snippets.

@JustinGrote
Last active October 24, 2022 14:56
Show Gist options
  • Save JustinGrote/fc60a31b0cdf8578954bf9478eaf8f7d to your computer and use it in GitHub Desktop.
Save JustinGrote/fc60a31b0cdf8578954bf9478eaf8f7d to your computer and use it in GitHub Desktop.
A companion to Select-String to more easily retrieve the results of capture groups, similar to $matches but pipeline friendly.
filter Select-StringMatch {
<#
.SYNOPSIS
A companion to select-string to quickly fetch the value of a capture group, named or indexed
.DESCRIPTION
With -replace or -match we have the simple $matches['group'] syntax, but this doesn't really exist for Select-String
and the typical options are not pipeline friendly. The default object returned from Select-String is not that friendly
either if all you want is the value(s) of a capture group to then pipe to another command. This is basically a pipeline
friendly version of $matches.
.EXAMPLE
@(
'this part does not matter. this does a'
'this part does not matter. this does b'
'this does c. this part does not matter'
)
| Select-String 'this does .'
| Select-StringMatch
WHAT IT DOES: Fetches whatever text begins with 'this does ' followed by any character
------
RESULT
------
this does a
this does b
this does c
.EXAMPLE
@(
'this part does not matter. this does a'
'this part does not matter. this does b'
'this does c. this part does not matter'
)
| Select-String 'this does (.)'
| Select-StringMatch -CaptureGroup 1
WHAT IT DOES: We put the letter we want in a capture group, and then fetch the first capture group.
------
RESULT
------
a
b
c
.EXAMPLE
@(
'this part does not matter. this does a'
'this part does not matter. this does b'
'this does c. this part does not matter.'
)
| Select-String 'this does (?<letter>.)'
| Select-StringMatch -CaptureGroup letter
WHAT IT DOES: Uses a named capture group 'letter' to define what we want rather than an index.
------
RESULT
------
a
b
c
.EXAMPLE
'first name joe','first name bob','first name frank'
| Select-String '(?<=first name ).+'
| Select-StringMatch
WHAT IT DOES: Uses a lookbehind to look for text that is preceded by "first name", and only return what comes after it as a capture group
------
RESULT
------
joe
bob
frank
.EXAMPLE
'joe franklin smith','billy william bob','jim westinghouse jones'
| Select-String '(?<first>\w+) (?<middle>\w+) (?<last>\w+)'
| Select-StringMatch 'middle'
WHAT IT DOES: Uses a regex to parse a name into 3 parts with capture groups, and retrieve only the middle names.
------
RESULT
------
franklin
william
westinghouse
.EXAMPLE
'joe franklin smith','billy william bob','jim westinghouse jones'
| Select-String '(?<first>\w+) (?<middle>\w+) (?<last>\w+)'
| Select-Object -First 1
| Select-StringMatch -AsHashTable
WHAT IT DOES: Uses a regex to parse a name into 3 parts with capture groups, and return the first result as a hashtable.
The order of the hashtable keys is the order in which they appear in the regex.
------
RESULT
------
Name Value
---- -----
middle franklin
0 joe franklin smith
first joe
last smith
.EXAMPLE
'joe franklin smith','billy william bob','jim westinghouse jones'
| Select-String '(?<first>\w+) (?<middle>\w+) (?<last>\w+)'
| Select-Object -First 1
| Select-StringMatch -AsHashtable -SortHashtable
WHAT IT DOES: Uses a regex to parse a name into 3 parts with capture groups, and return the first result as a hashtable.
The order of the hashtable keys is in alphabetic order
------
RESULT
------
Name Value
---- -----
0 joe franklin smith
first joe
last smith
middle franklin
.EXAMPLE
'joe franklin smith','billy william bob','jim westinghouse jones'
| Select-String '(?<first>\w+) (\w+) (?<last>\w+)'
| Select-Object -First 1
| Select-StringMatch -AsHashtable
WHAT IT DOES: Demonstrates a mix of named and unnamed groups. Middle name ends up being stored as the 1st capture group as it is unnamed
------
RESULT
------
Name Value
---- -----
0 joe franklin smith
1 franklin
first joe
last smith
#>
[CmdletBinding()]
param(
#Which capture group value to select. If not specified it will return the matched section of the string (index 0). Examples: 1 for first () capture group, 2 for second () capture group, test for named capture group (<?:test>), etc.
[string[]]$CaptureGroup,
#A list of MatchInfo objects. You should typically pipe this in from Select-String
[Parameter(ValueFromPipeline)][Microsoft.PowerShell.Commands.MatchInfo]$MatchInfo,
#If specified, returns as a hashtable of capture group values.
[Switch]$AsHashtable,
#By default, the hashtable is ordered by the occurence of the capture group in the regex. If you want then to be ordered alphabetically instead, specify this option.
[Switch]$SortHashtable
)
$groups = $PSItem.matches[0].Groups
#Return the matched string result if CaptureGroup is not specified
$result = if ($CaptureGroup.count -eq 0 ) {
if ($AsHashtable) {
$groups
} else {
$groups[0]
}
} else {
foreach ($key in $CaptureGroup) {
if ($groups.containsKey($key)) {
$groups[$key]
}
}
}
if ($AsHashtable) {
$groupHashtable = [ordered]@{}
if ($SortHashtable) {$result = Sort-Object -InputObject $result -Property Name}
foreach ($groupItem in $result) {
$groupHashtable[$groupItem.Name] = $groupItem.Value
}
return $groupHashtable
}
return $result.Value
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment