1

I'm trying to create a PSCustomObject from a robocopy log file. The first piece is pretty easy but I'm struggling with the $Footer part. I can't seem to find a good way to split up the values.

It would be nice if every entry has it's own Property, so it's possible to use for example $Total.Dirs or $Skipped.Dirs. I was thinking about Import-CSV, because that's just great on how it allows you to have column headers. But this doesn't seem to fit here. There's another solution I found here but it seems a bit of overkill.

Code:

Function ConvertFrom-RobocopyLog {
    Param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
        [String]$LogFile
    )

    Process {
        $Header = Get-Content $LogFile | select -First 10
        $Footer = Get-Content $LogFile | select -Last 7

        $Header | ForEach-Object {
            if ($_ -like "*Source*") {$Source = (($_.Split(':'))[1]).trim()}
            if ($_ -like "*Dest*")   {$Destination = (($_.Split(':'))[1]).trim()}
        }

        $Footer | ForEach-Object {
            if ($_ -like "*Dirs*") {$Dirs = (($_.Split(':'))[1]).trim()}
            if ($_ -like "*Files*") {$Files = (($_.Split(':'))[1]).trim()}
            if ($_ -like "*Times*") {$Times = (($_.Split(':'))[1]).trim()}
        }

        $Obj = [PSCustomObject]@{
                'Source'      = $Source
                'Destination' = $Destination
                'Dirs'        = $Dirs
                'Files'       = $Files
                'Times'       = $Times
        }               
        Write-Output $Obj
    }
}

Log file:

-------------------------------------------------------------------------------
   ROBOCOPY     ::     Robust File Copy for Windows                              
-------------------------------------------------------------------------------

  Started : Wed Apr 01 14:28:11 2015

   Source : \\SHARE\Source\
     Dest : \\SHARE\Target\

    Files : *.*

  Options : *.* /S /E /COPY:DAT /PURGE /MIR /Z /NP /R:3 /W:3 

------------------------------------------------------------------------------
         0 Files...
         0 More Folders and files...

------------------------------------------------------------------------------

               Total    Copied   Skipped  Mismatch    FAILED    Extras
    Dirs :         2         0         2         0         0         0
   Files :       203         0       203         0         0         0
   Bytes :         0         0         0         0         0         0
   Times :   0:00:00   0:00:00                       0:00:00   0:00:00

   Ended : Wed Apr 01 14:28:12 2015

Thank you for your help.

DarkLite1
  • 13,637
  • 40
  • 117
  • 214
  • I know what you are asking but dont' know what you expect for output. Do you have a property called Dirs that has sub properties for total, copied etc. – Matt Apr 01 '15 at 14:58

2 Answers2

4

You can clean this up more but this is the basic approach I would take.

Function ConvertFrom-RobocopyLog {
    Param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
        [String]$LogFile
    )

    Process {
        $Header = Get-Content $LogFile | select -First 10
        $Footer = Get-Content $LogFile | select -Last 7

        $Header | ForEach-Object {
            if ($_ -like "*Source*") {$Source = (($_.Split(':'))[1]).trim()}
            if ($_ -like "*Dest*")   {$Destination = (($_.Split(':'))[1]).trim()}
        }

        $Footer | ForEach-Object {
            if ($_ -like "*Dirs*"){
                $lineAsArray = (($_.Split(':')[1]).trim()) -split '\s+'
                $Dirs = [pscustomobject][ordered]@{
                    Total =  $lineAsArray[0]   
                    Copied =  $lineAsArray[1]  
                    Skipped =  $lineAsArray[2]  
                    Mismatch = $lineAsArray[3]     
                    FAILED = $lineAsArray[4]     
                    Extras = $lineAsArray[5] 
                }
            }
            if ($_ -like "*Files*"){
                $lineAsArray = ($_.Split(':')[1]).trim() -split '\s+'
                $Files = [pscustomobject][ordered]@{
                    Total =  $lineAsArray[0]   
                    Copied =  $lineAsArray[1]  
                    Skipped =  $lineAsArray[2]  
                    Mismatch = $lineAsArray[3]     
                    FAILED = $lineAsArray[4]     
                    Extras = $lineAsArray[5] 
                }
            }
            if ($_ -like "*Times*"){
                $lineAsArray = ($_.Split(':',2)[1]).trim() -split '\s+'
                $Times = [pscustomobject][ordered]@{
                    Total =  $lineAsArray[0]   
                    Copied =  $lineAsArray[1]     
                    FAILED = $lineAsArray[2]     
                    Extras = $lineAsArray[3] 
                }
            }
        }

        $Obj = [PSCustomObject]@{
                'Source'      = $Source
                'Destination' = $Destination
                'Dirs'        = $Dirs
                'Files'       = $Files
                'Times'       = $Times
        }               
        Write-Output $Obj
    }
}

I wanted to make a function to parse the footer lines but $Times is a special case since it does not have all the same columns of data.

With $Times the important difference is how we are doing the split. Since the string contains more than one colon we need to account for that. Using the other paramenter in .Split() we specify the number of elements to return.

$_.Split(':',2)[1]

Since these logs always have output and no blanks row elements we can do this assuming that the parsed elements of $lineAsArray will always have 6 elements.

Sample Output

Source      : \\SHARE\Source\
Destination : \\SHARE\Target\
Dirs        : @{Total=2; Copied=0; Skipped=2; Mismatch=0; FAILED=0; Extras=0}
Files       : @{Total=203; Copied=0; Skipped=203; Mismatch=0; FAILED=0; Extras=0}
Times       : @{Total=0:00:00; Copied=0:00:00; FAILED=0:00:00; Extras=0:00:00}

So if you wanted the total files copied you can now use dot notation.

(ConvertFrom-RobocopyLog C:\temp\log.log).Files.Total
203
Matt
  • 45,022
  • 8
  • 78
  • 119
  • Thank you Matt, you're the boss! I didn't know this (`$_.Split(':',2)`) was possible with `Split`. Really great solution! :) I understand now how it's done. – DarkLite1 Apr 02 '15 at 06:26
  • A small improvement I added was `Total = [TimeSpan]$Array[0]`. – DarkLite1 Apr 02 '15 at 06:36
  • If you need more than one value, save the results to a variable so you only read the log file once: `$robolog = ConvertFrom-RobocopyLog C:\temp\log.log` `($robolog).Files.Total` = `203` `($robolog).Files.FAILED` = `0` – DBADon Jan 19 '22 at 19:42
1

Not that clear what you want to do, but this will go some way to showing you how to get the stats into an array of objects

$statsOut = @()
$stats = Get-Content $LogFile | select -Last 6 | select -first 4

$stats | % {
  $s = $_ -split "\s+"
  $o = new-object -type pscustomobject -property @{"Name"=$s[0];"Total"=$s[2];"Copied"=$s[3];"Skipped"=$s[4];"mismatch"=$s[5]}; 
  $statsOut += ,$o 
}

Gives:

[PS] > $statsOut | ft -Auto
mismatch Name  Skipped Total   Copied
-------- ----  ------- -----   ------
0        Dirs  2       2       0
0        Files 203     203     0
0        Bytes 0       0       0
arco444
  • 22,002
  • 12
  • 63
  • 67