Home > database >  Match and $Matches in Powershell via RegEx
Match and $Matches in Powershell via RegEx

Time:03-18

I have a little issue with a Powershell project, I've been working on for some time now.

The basic idea is that six iPerf speed measurements will be executed. A logfile is created to have some data which can be displayed to the user.

But there's some issue with the match and variable Matches in Powershell to display multiple values..

There's the code, I've been working on..


    $1 = (Get-Content -Path 'iperf3.txt' -TotalCount 398)[-1] # Fetch details about speed measurements for download
    $2 = (Get-Content -Path 'iperf3.txt' -TotalCount 796)[-1] # Fetch details about speed measurements for upload
    $3 = (Get-Content -Path 'iperf3.txt' -TotalCount 1195)[-1] # Same as above
    $4 = (Get-Content -Path 'iperf3.txt' -TotalCount 1593)[-1] # Same as above
    $5 = (Get-Content -Path 'iperf3.txt' -TotalCount 1992)[-1] # Same as above
    $6 = (Get-Content -Path 'iperf3.txt' -TotalCount 2390)[-1] # Same as above

Output via Get-Content and TotalCount


    [SUM]   0.00-30.00  sec  1.09 GBytes   313 Mbits/sec                  receiver
    [SUM]   0.00-30.00  sec  1.09 GBytes   312 Mbits/sec                  receiver
    [SUM]   0.00-30.00  sec  1.11 GBytes   317 Mbits/sec                  receiver
    [SUM]   0.00-30.00  sec  1.09 GBytes   311 Mbits/sec                  receiver
    [SUM]   0.00-30.00  sec  1.11 GBytes   317 Mbits/sec                  receiver
    [SUM]   0.00-30.00  sec  1.09 GBytes   312 Mbits/sec                  receiver

Afterwards, I use the RegEx and Variable to output the numbers before Mbits/sec and include Mbits/sec from this line of code..

    $1 -match '\d \sMbits[/]sec'
    $2 -match '\d \sMbits[/]sec'

etc.

I do variable Matches to validate that the Variable is True, and receive the output of 312 Mbits/sec, but nothing more.

Now this is where I cannot see the fault in the code. The variable passed and it's true, but I only have one Value as 313 Mbits/sec via Value.

I figured that I would see both 313 Mbits/sec and 312 Mbits/sec in the output/value prompt.

Did I do something wrong while using the match/Matches variable/function?

Any feedback and/or suggestions will be appreciated.

CodePudding user response:

The automatic $Matches variable only ever reflects the results of the most recent -match operation - and then only if (a) the matching was successful and (b), fundamentally, only if the LHS was a single string - if the LHS was a collection (array), -match acts as a filter, returning the subarray of matching elements, and does not populate $Matches.

However, your command can be greatly streamlined:

  • Use a single Get-Content call
  • Use a Select-Object call with the -Index parameter to extract the lines of interest (indices are 0-based).
  • Use the -replace operator instead of -match in order to directly extract the substrings of interest:
(
  Get-Content 'iperf3.txt' | Select-Object -Index 397,795,1194,1592,1991,2389
) -replace '. \b(\d \sMbits/sec). ', '$1' 

Taking a step back:

Instead of selecting the lines of interest by fixed indices (line numbers), select them by regexes too, which allows you to use a single Select-String call:

Select-String -LiteralPath 'iperf3.txt' -Pattern '\s*\[SUM]. \b(\d \sMbits/sec). ' |
  ForEach-Object {
    $_.Matches.Groups[1].Value
  }

CodePudding user response:

You didn't do anything wrong it's just the default behavior for the -match operator and how the $Matches automatic variable is populated.

Here is an extract from Matching operators that explains very well how it works:

It is important to note that the $Matches hashtable contains only the first occurrence of any matching pattern.

You have 2 workarounds, the first one could be using Regex.Matches Method to find all appearances of the matched pattern:

$string = @'
[SUM]   0.00-30.00  sec  1.09 GBytes   313 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.09 GBytes   312 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.11 GBytes   317 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.09 GBytes   311 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.11 GBytes   317 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.09 GBytes   312 Mbits/sec                  receiver
'@

[regex]::Matches($string, '\d \sMbits[/]sec').Value

Note that, in above example, $string is a multi-line string, however in the example it will be an array since it requires a loop.

$string = @'
[SUM]   0.00-30.00  sec  1.09 GBytes   313 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.09 GBytes   312 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.11 GBytes   317 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.09 GBytes   311 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.11 GBytes   317 Mbits/sec                  receiver
[SUM]   0.00-30.00  sec  1.09 GBytes   312 Mbits/sec                  receiver
'@ -split '\r?\n'

foreach($line in $string) {
    if($line -match '\d \sMbits[/]sec') {
        $Matches[0]
    }
}

GitHub issue #7867 proposes to add a -matchall operator to PowerShell, if you believe it would be helpful consider up-voting it.

  • Related