34

Let's say I have the string "blah blah F12 blah blah F32 blah blah blah" and I want to match the F12 and F32, how would I go about capturing both to the Powershell magic variable $matches?

If I run the following code in Powershell:

$string = "blah blah F12 blah blah F32 blah blah blah"
$string -match "F\d\d"

The $matches variable only contains F12

I also tried:

$string -match "(F\d\d)"

This time $matches had two items, but both are F12

I would like $matches to contain both F12 and F32 for further processing. I just can't seem to find a way to do it.

All help would be greatly appreciated. :)

Etzeitet
  • 1,995
  • 2
  • 18
  • 22
  • 2
    anyone wondering why `-match` returns two _F12_ results in the second attempt, it's because _F12_ is both the result of the _whole_ match expression AND the result of the first match group in the expression (the result of the parentheses... – Code Jockey May 20 '16 at 14:46

4 Answers4

61

You can do this using Select-String in PowerShell 2.0 like so:

Select-String F\d\d -input $string -AllMatches | Foreach {$_.matches}

A while back I had asked for a -matchall operator on MS Connect and this suggestion was closed as fixed with this comment:

"This is fixed with -allmatches parameter for select-string."

Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 1
    So how do you reference the captured values, from Select-String -AllMatches? – Nathan Hartley May 08 '12 at 19:38
  • 11
    @NathanHartley `$string | Select-String "(F\d\d)" -AllMatches | % matches | % Value` – Keith Hill May 08 '12 at 19:53
  • Definitely not ideal, since it won't match across new lines. – lordcheeto Feb 07 '18 at 19:11
  • 1
    @lordcheeto - what if you tried a pattern like: "(?m)(F\d\d)" - the initial (?m) tells regex engine to do multi-line match... – user1390375 May 11 '18 at 17:47
  • 1
    @user1390375 Doesn't appear to work that way from the `Select-String` command. – lordcheeto May 12 '18 at 06:13
  • This is great news I didn't know select-string could be used that way thanks! – Rakha Jan 14 '20 at 14:34
  • 1
    (Many years later) [member-access enumeration](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Member-Access_Enumeration) now allows you to simplify to: `("blah blah F12 blah blah F32 blah blah blah" | Select-String -AllMatches F\d\d).Matches.Value`. The suggestion to introduce a `-matchall` operator (as an _expression_-based complement to the _pipeline_-based `-AllMatches`) has made a comeback in [GitHub issue #7867](https://github.com/PowerShell/PowerShell/issues/7867) – mklement0 Apr 06 '23 at 12:45
33

I suggest using this syntax as makes it easier to handle your array of matches:

$string = "blah blah F12 blah blah F32 blah blah blah" ;
$matches = ([regex]'F\d\d').Matches($string);
$matches[1].Value; # get matching value for second occurance, F32
sonjz
  • 4,870
  • 3
  • 42
  • 60
  • 2
    I wouldn't use the reserved variable name **$matches**, but an own variable like **$m**. And if capturing groups are present, to show all values of, for example group 1, use: `$m | % {$_.groups[1].value}` – jamacoe Aug 11 '22 at 05:57
3

I see 2 scenarios that are handled differently:

  1. extracting all matches of a single pattern
  2. extracting single match of multiple patterns

1. extract all matches of one pattern: select-string + -allmatches

  • e.g. regex: (?<=jobs).*
  • counter-intuitive but you need to use Select-String to handle this like I am to get ids of nomad jobs from the output exemplified below
$m = "Watch the deployment in realtime at: https://nomad.foo.net/ui/jobs/20e183af-8243-11eb-a2af-0a58a9feac2a
08:23
Watch the deployment in realtime at: https://nomad.foo.net/ui/jobs/20e130e9-8243-11eb-a2af-0a58a9feac2a"
$r = "(?<=jobs/).*"
$l = Select-String $r -InputObject $m -AllMatches | 
    Foreach {$_.matches.Value}
20e183af-8243-11eb-a2af-0a58a9feac2a
20e130e9-8243-11eb-a2af-0a58a9feac2a
$l[0]
>>> 20e183af-8243-11eb-a2af-0a58a9feac2a

2. extract a single/first match of one/multiple patterns: capture groups and $Match[]

▶ $s = "Hello World from Mr Pavol"
▶ $r = "(World).*(Pavol)"
▶ $s -match $r
True
▶ $Matches
Name                           Value
----                           -----
2                              Pavol
1                              World
0                              World from Mr Pavol
pavol.kutaj
  • 401
  • 1
  • 7
  • 14
0
$String = @'
MemberProgram PackageID="12345678" ProgramName="Install"/
MemberProgram PackageID="87654321" ProgramName="Install"/
MemberProgram PackageID="21436587" ProgramName="Install"/
MemberProgram PackageID="78563412" ProgramName="Install"/
'@
([regex]'(?<=PackageID=\")\d+(?=\")').Matches($String).value
BiaoGuo
  • 39
  • 3
  • Thanks for your answer. To improve the quality and avoid getting downvotes, please add additional commentary about how your answer solves the problem. Simply pasting in some source code is not the best way to answer. Thanks! – Gene Z. Ragan Sep 24 '19 at 17:56
  • 3
    Sorry, I am from China, my English is not very good, I don't know how to write a description. Even if this reply is done with the help of Google Translate.T_T – BiaoGuo Sep 24 '19 at 18:21