Powershell - Regular Expression Multiple Matches Powershell - Regular Expression Multiple Matches powershell powershell

Powershell - Regular Expression Multiple Matches


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)'


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()} 


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