10

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: http://regex101.com/r/jQ6uC8/6

$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: http://pastebin.com/BaywGtFE

David Clarke
  • 12,888
  • 9
  • 86
  • 116
Frankstar
  • 306
  • 1
  • 3
  • 10
  • Welcome to Stack Overflow, @Frankstar. Please put as much information as you can directly in the question. I got the regex from regex101 and put it here. (The links to regex101 are great though!). – aliteralmind Jul 19 '14 at 00:12
  • Hello @aliteralmind ! I tryed my best - thank you for your update. Will keep that in mind for future questions. – Frankstar Jul 19 '14 at 08:22
  • **See Also**: [How to capture multiple regex matches, from a single line, into the $matches magic variable in Powershell?](https://stackoverflow.com/questions/3141851/how-to-capture-multiple-regex-matches-from-a-single-line-into-the-matches-mag) – KyleMit May 20 '20 at 14:17

4 Answers4

17

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)'
Matt
  • 45,022
  • 8
  • 78
  • 119
  • hi @Matt. this is the best answer for my question so far and it works like a charm with the test String i provided! Thank you !! anyway i have the same problem with the real-time Data (it look the same to me ...) i just get back the first line of the match. as example `Device #0` `Device #1` – Frankstar Jul 19 '14 at 08:46
  • i think its something with the Line feed or carriage return. maybe i need too convert the output from the commandline tool / exe. my string is stored in here `$getdevice = .\arcconf.exe getconfig $cid PD` anyway the output _looks_ the same – Frankstar Jul 19 '14 at 08:47
  • Just found the Solution. i piped the Output trough `Out-Sting` Example: `$getdevice = .\arcconf.exe getconfig $cid PD | Out-String` now its working. – Frankstar Jul 19 '14 at 08:56
7

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()
} 
zx81
  • 41,100
  • 9
  • 89
  • 105
  • ty for your help. anyway i cant get this to work with my data. The match array is always empty. will try too figure out whats wrong. – Frankstar Jul 19 '14 at 08:37
2

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                
mjolinor
  • 66,130
  • 7
  • 114
  • 135
  • Excellent job converting the strings to a hash and nesting -replace thereby formatting it into a very versatile object. This showed me how to use `PSObject` and `ConvertFrom-StringData` – Matt Jul 19 '14 at 02:19
  • wow, didnt expect such a nice solution. anyway i get some erros with the "real time" Input/sring. http://pastebin.com/0uChjGa1. But its working with the sample Output/String i provided, so the answere is right. just need too figure out the problem and if i can continue work with that answere/solution. ty – Frankstar Jul 19 '14 at 08:33
  • as posted too Matts answer .. i piped the commandline Output trough `Out-String` and now the Output looks MUCH better and right. Anyway i got one error. `ConvertFrom-StringData : Datenzeile " Device is an Enclosure services device" weist nicht das "Name=Wert"-Format auf.` dont know how to get rid of it and i dont know whats different with `Device is an Enclosure services device` string. – Frankstar Jul 19 '14 at 08:59
  • sry for the comment spam - but i figured it out. its simple the replacement string, you searched for "Device is a" and in that special output its "Device is a **n**". Just adjusted the -replace and now its working finde. ty – Frankstar Jul 19 '14 at 09:28
1

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
saw-next
  • 11
  • 1