PowerShell: Extract the summary table from RoboCopy’s log file

When automating the copying of files with RoboCopy I wanted to be able to interrogate the summary table at the bottom of its log file - shown below.

            Total    Copied   Skipped  Mismatch    FAILED    Extras
 Dirs :         1         0         1         0         0         0
Files :         2         2         0         0         0         0
Bytes :  328.45 m  328.45 m         0         0         0         0

To allow me to analyse the summary table I wrote a PowerShell Cmdlet that extracts it out into PowerShell objects, which I can then feed into other processes/scripts for analysis.

This means tasks like ordering the summary table or finding any failures are now really simple:

> Get-Content "C:\robocopy.log" -Raw | Select-RoboSummary | Sort-Object Type | Format-Table

Type       Total       Copied       Skipped       Mismatch       Failed       Extras                          
----       -----       ------       -------       --------       ------       ------                          
Bytes      328.45 m    328.45 m     0             0              0            0                               
Dirs       1           0            1             0              0            0                               
Files      2           2            0             0              1            0                     
> Get-Content "C:\robocopy.log" -Raw | Select-RoboSummary | Where{ $_.Failed -gt 0 }
function Select-RoboSummary {
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [string]$log,
        [parameter(Mandatory=$false,ValueFromPipeline=$false)]
        [switch]$separateUnits
    )
    PROCESS
    {
        $cellHeaders = @("Total", "Copied", "Skipped", "Mismatch", "Failed", "Extras")
        $rowTypes    = @("Dirs", "Files", "Bytes")

        # Extract rows
        $rows = $log | Select-String -Pattern "(Dirs|Files|Bytes)\s*:(\s*([0-9]+(\.[0-9]+)?( [a-zA-Z]+)?)+)+" -AllMatches
        if ($rows.Count -eq 0)
        {
            throw "Summary table not found"
        }

        if ($rows.Matches.Count -ne $rowTypes.Count)
        {
            throw "Unexpected number of rows/ Expected {0}, found {1}" -f $rowTypes.Count, $rowsMatch.Count
        }

        # Merge each row with its corresponding row type, with property names of the cell headers
        for($x = 0; $x -lt $rows.Matches.Count; $x++)
        {
            $rowType  = $rowTypes[$x]
            $rowCells = $rows.Matches[$x].Groups[2].Captures | foreach{ $_.ToString().Trim() }

            if ($cellHeaders.Length -ne $rowCells.Count)
            {
                throw "Unexpected amount of cells in a row. Expected {0} cells (the amount of headers) but found {1}" -f $cellHeaders.Length,$rowCells.Count
            }

            $row = New-Object -TypeName PSObject
            $row | Add-Member -Type NoteProperty Type($rowType)

            for($i = 0; $i -lt $rowCells.Count; $i++)
            {
                $header = $cellHeaders[$i]
                $cell   = $rowCells[$i]

                if ($separateUnits -and ($cell -match " "))
                {
                    $cell = $cell -split " "
                }

                $row | Add-Member -Type NoteProperty -Name $header -Value $cell
            }

            $row
        }
    }
}