Powershell - Regular Expression Multiple Matches

Maybe my reasoning is faulty, but I can't get this working.

Here's my regex: (Device\s#\d(\n.*)*?(?=\n\s*Device\s#|\Z))

Try it:

$getdevice is the input string. I'm getting this string from the Stream/Output from a command line tool.

$dstate = $getdevice | select-string -pattern '(Device\s#\d(\n.*)*?(?=\n\s*SSD\s+|\Z))' -AllMatches | % { $_ -match '(Device\s#\d(\n.*)*?(?=\n\s*SSD\s+|\Z))' > $null; $matches[0] } Write-Host $dstate 

Output:

Device #0 Device #1 Device #2 Device #3 Device #4

Same output for the $matches[1], $matches[2] is empty.

Is there a way I can get all matches, like on regex101.com? I'm trying to split the Output/String into separate variables (one for Device0, one for Device1, Device2, and so on).

Update: Here's the Output from the command line tool:

3

4 Answers

I used your sample data in a here-string for my testing. This should work although it can depend on where your sample data comes from.

Using powershell 3.0 I have the following

$getdevice | select-string -pattern '(?smi)(Device\s#\d+?(.*?)*?(?=Device\s#|\Z))' -AllMatches | ForEach-Object {$_.Matches} | ForEach-Object {$_.Value} 

or if your PowerShell Verison supports it...

($getdevice | select-string -pattern '(?smi)(Device\s#\d+?(.*?)*?(?=Device\s#|\Z))' -AllMatches).Matches.Value 

Which returns 4 objects with their device id's. I don't know if you wanted those or not but the regex can be modified with lookarounds if you don't need those. I updated the regex to account for device id with more that one digit as well in case that happens.

The modifiers that I used

  1. s modifier: single line. Dot matches newline characters
  2. m modifier: multi-line. Causes ^ and $ to match the begin/end of each line (not only begin/end of string)
  3. i modifier: insensitive. Case insensitive match (ignores case of [a-zA-Z])

Another regex pattern thats works in this way that is shorter

'(?smi)(Device\s#).*?(?=Device\s#|\Z)' 
3

With your existing regex, to get a list of all matches in a string, use one of these options:

Option 1

$regex = [regex] '(Device\s#\d(\n.*)*?(?=\n\s*Device\s#|\Z))' $allmatches = $regex.Matches($yourString); if ($allmatches.Count > 0) { # Get the individual matches with $allmatches.Item[] } else { # Nah, no match } 

Option 2

$resultlist = new-object System.Collections.Specialized.StringCollection $regex = [regex] '(Device\s#\d(\n.*)*?(?=\n\s*Device\s#|\Z))' $match = $regex.Match($yourString) while ($match.Success) { $resultlist.Add($match.Value) | out-null $match = $match.NextMatch() } 
1

While it doesn't exactly answer your question, I'll offer a slightly different approach:

($getdevice) -split '\s+(?=Device #\d)' | select -Skip 1 

Just for fun,

$drives = ($getdevice) -split '\s+(?=Device #\d)' | select -Skip 1 | foreach { $Stringdata = $_.replace(' : ','=') -replace 'Device #(\d)','Device = $1' -Replace 'Device is a (\w+)','DeviceIs = $1' New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata) } $drives | select Device,DeviceIs,'Total Size' Device DeviceIs Total Size ------ -------- ---------- 0 Hard drive 70007 MB 1 Hard drive 70007 MB 2 Hard drive 286102 MB 3 Hard drive 286102 MB 
4

try this variant:

[regex]::Matches($data,'(?im)device #\d((?!\s*Device #\d)\r?\n.)*?') | select value Value ----- Device #0 Device #1 Device #2 Device #3 Device #4 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like