PoshCode Logo PowerShell Code Repository

Robocopy Log analyser by hhhhh 8 weeks ago (modification of post by Bart Lievers view diff)
diff | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/6764"></script>download | new post

A function to analyse Robocopy logs which are placed in a folder.
It returns a custom PS object containing three properties: – filename of the CSV file where the results are writen to. – RC like summary of all the RC Logs. – The results, same data as in the CSV file.

  1. function Analyse-RC_Log {
  2.     <#
  3.     .SYNOPSIS
  4.         Robocopy log analyser
  5.     .DESCRIPTION
  6.         analysing the robocopy logs that are generated with the /LOG option.
  7.         It has two modes, anaylsing the summary of log files and analyse the full log.
  8.         The report is saved as a CSV file.
  9.         Returns an custom object containing a RC summary like property, a CSV property.
  10.         During script process the Culture of the script is changed to en-US for date and number interpretation / calculations
  11.  
  12.         Script is based on Robocopy log analyser from http://www.chapmancentral.co.uk/cloudy/2013/02/23/parsing-robocopy-logs-in-powershell/
  13.     .EXAMPLE
  14.         >Analyse-RC_Log -SourcePath d:\RC_logdir -ExcelCSV -fp -unitsize GB
  15.         Analyse log files in d:\RC_logdir, use a semicolon in the CSV file (conform MS Excel). Parse the complete log files and report al Bytes sizes as GB
  16.     .EXAMPLE
  17.         Give another example of how to use it
  18.     .PARAMETER fp
  19.         File Parsing. Parse the complete file instead of the heather and footer
  20.     .PARAMETER SourcePath
  21.         Path where the Robocopy logs are saved.
  22.     .PARAMETER ExcelCSV
  23.         Use a semicolon as seperator
  24.     .Parameter UnitSize
  25.         Report al Byte sizes  in given unit size.
  26.     .Link
  27.         http://www.chapmancentral.co.uk/cloudy/2013/02/23/parsing-robocopy-logs-in-powershell/
  28.     .NOTES
  29.         File Name      : Analyse-RC_Log.ps1
  30.         Author         : B. Lievers
  31.         Prerequisite   : PowerShell V2 over Vista and upper.
  32.         Copyright 2015 - Bart Lievers
  33.     #>
  34.     [CmdletBinding()]
  35.  
  36.     param(
  37.             [parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false,HelpMessage='Source Path with no trailing slash')][string]$SourcePath,
  38.             [switch]$fp,
  39.         [Switch]$ExcelCSV,
  40.         [Parameter(HelpMessage='Unit size, default is Bytes when parameter is not present')][ValidateSet("B","GB","KB","MB","TB")][string]$UnitSize
  41.             )
  42.  
  43.     begin {
  44.         [System.Globalization.CultureInfo]$culture=[System.Globalization.CultureInfo]("en-US")
  45.         $OldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
  46.         trap
  47.         {
  48.             [System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture
  49.         }
  50.         [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
  51.         Write-Verbose ("Changing Locale from "+$oldCulture.Name+" to "+$culture.Name)
  52.  
  53.         write-host "Robocopy log parser. $(if($fp){"Parsing file entries"} else {"Parsing summaries only, use -fp to parse file entries"})"
  54.  
  55.         $ElapsedTime = [System.Diagnostics.Stopwatch]::StartNew()
  56.         $refreshrate=1 # progress counter refreshes this often when parsing files (in seconds)
  57.  
  58.         #region initialize header fields
  59.         # These summary fields always appear in this order in a robocopy log
  60.         $HeaderParams = @{
  61.                 "04|Started" = "date"
  62.                 "01|Source" = "string";
  63.                 "02|Dest" = "string";
  64.                 "03|Options" = "string";
  65.                 "07|Dirs" = "counts";
  66.                 "08|Files" = "counts";
  67.                 "09|Bytes" = "counts";
  68.                 "10|Times" = "counts";
  69.                 "05|Ended" = "date"
  70.                 #"06|Duration" = "string"
  71.         }
  72.         #-- summary fields for file tag statistics during file parsing, swich -fp
  73.         $fileTags=@{
  74.             "01|MISMATCH" = ""
  75.             "02|EXTRA file" = ""
  76.             "03|New File" = ""
  77.             "04|lonely" = ""
  78.             "05|Newer" = ""
  79.             "06|Newer XN" = ""
  80.             "07|Older" = ""
  81.             "08|Older XO" = ""
  82.             "09|Changed" = ""
  83.             "10|Changed XC" = ""
  84.             "11|Tweaked" = ""
  85.             "12|Same IS" = ""
  86.             "13|Same" = ""
  87.             "14|attrib" = ""
  88.             "15|named" = ""
  89.             "16|large" = ""
  90.             "17|small" = ""
  91.             "18|too old" = ""
  92.             "19|too new" = ""
  93.             "20|New Dir"= ""
  94.         }
  95.         #-- summary fields for directory tag statistics during file parsing, swich -fp
  96.         $DirTags=@{
  97.             "01|MISMATCH" = ""
  98.             "02|Extra Dir" = ""
  99.             "03|New Dir" = ""
  100.             "04|lonely" = ""
  101.             "05|named" = ""
  102.             "06|junction" = ""
  103.             "07|exist" = ""
  104.         }
  105.  
  106.         $ProcessCounts = @{
  107.                 "Processed" = 0;
  108.                 "Error" = 0;
  109.                 "Incomplete" = 0
  110.         }
  111.         #endregion
  112.  
  113.         #-- Default the CSV delim is a comma, when using CSV for Excel a semicolon is needed as delimmiter
  114.         if ($ExcelCSV) { $Delim=";"} else {$Delim=","}
  115.          #-- ASCII tab character
  116.         $tab=[char]9
  117.  
  118.          #-- Get list of files to analyse
  119.         $files=get-childitem $SourcePath
  120.         #-- let's start writing, shall we ?
  121.         $writer=new-object System.IO.StreamWriter("$(get-location)\robocopy-$(get-date -format "dd-MM-yyyy_HH-mm-ss").csv")
  122.  
  123.  
  124.         #region private functions                                                                                                                                                                                                                                                                                                                                                                                                                          #region private functions
  125.                                                                                                                                    
  126.         function Get-Tail{
  127.             <#
  128.             .SYNOPSIS
  129.                 Get tail of file
  130.             .EXAMPLE
  131.                 >Get-Tail $reader 20
  132.             .PARAMETER reader
  133.                 an IO stream file object
  134.             .PARAMETER count
  135.                 Number of rows to collect
  136.             #>
  137.             Param (
  138.                 [object]$reader,
  139.                 [int]$count = 10
  140.                 )
  141.  
  142.                 $lineCount = 0
  143.                 [long]$pos = $reader.BaseStream.Length - 1
  144.  
  145.                 while($pos -gt 0) {
  146.                         $reader.BaseStream.position=$pos
  147.  
  148.                         # 0x0D (#13) = CR
  149.                         # 0x0A (#10) = LF
  150.                         if ($reader.BaseStream.ReadByte() -eq 10) {
  151.                                 $lineCount++
  152.                                 if ($lineCount -ge $count) { break }
  153.                             }
  154.                         $pos--
  155.                     }
  156.  
  157.                 # tests for file shorter than requested tail
  158.                 if ($lineCount -lt $count -or $pos -ge $reader.BaseStream.Length - 1) {
  159.                         $reader.BaseStream.Position=0
  160.                 } else {
  161.                         # $reader.BaseStream.Position = $pos+1
  162.                 }
  163.  
  164.                 $lines=@()
  165.                 while(!$reader.EndOfStream) {
  166.                         $lines += $reader.ReadLine()
  167.                 }
  168.                 return $lines
  169.         }
  170.  
  171.         function Get-Top {
  172.             <#
  173.             .SYNOPSIS
  174.                 Get top of file
  175.             .EXAMPLE
  176.                 >Get-Top $reader 20
  177.             .PARAMETER reader
  178.                 an IO stream file object
  179.             .PARAMETER count
  180.                 Number of rows to collect
  181.             #>
  182.             Param(
  183.                 [object]$reader,
  184.                 [int]$count = 10
  185.             )
  186.  
  187.                 $lines=@()
  188.                 $lineCount = 0
  189.                 $reader.BaseStream.Position=0
  190.                 while(($linecount -lt $count) -and !$reader.EndOfStream) {
  191.                         $lineCount++
  192.                         $lines += $reader.ReadLine()           
  193.                 }
  194.                 return $lines
  195.         }
  196.  
  197.         function Remove-Key{
  198.             <#
  199.             .SYNOPSIS
  200.                 Return the name without the ID
  201.             .EXAMPLE
  202.                 >Remove-Key -name "01|example"
  203.             .PARAMETER name
  204.                 a string where the ID needs to be stripped
  205.             #>
  206.             Param ( $name
  207.             )
  208.                 if ( $name -match "|") {
  209.                         return $name.split("|")[1]
  210.                 } else {
  211.                         return ( $name )
  212.                 }
  213.         }
  214.  
  215.         function Get-Value{
  216.             <#
  217.             .SYNOPSIS
  218.                 Get the value of a RC line
  219.             .EXAMPLE
  220.                 >Get-Value -line " filecount : 35555" -variable "Filecount"
  221.                 Returns 35555
  222.             .PARAMETER line
  223.                 A RC log string
  224.             .PARAMETER variable
  225.                 The variable that needs to be extracted
  226.             #>
  227.             Param(
  228.                 $line,
  229.                 $variable
  230.             )
  231.                 if ($line -like "*$variable*" -and $line -like "* : *" ) {
  232.                         $result = $line.substring( $line.IndexOf(":")+1 )
  233.                         return $result
  234.                 } else {
  235.                         return $null
  236.                 }
  237.         }
  238.  
  239.         function UnBodge-Date{
  240.             <#
  241.             .SYNOPSIS
  242.                 Convert Robocopy date to a usable format
  243.             .EXAMPLE
  244.                 >UnBodge-Date -dt "Sat Feb 16 00:16:49 2013"
  245.                 Returns 16-02-2013 00:16:49 in Locale format
  246.             .PARAMETER dt
  247.             #>
  248.             Param(
  249.                 $dt
  250.             )
  251.                 # Fixes RoboCopy botched date-times in format Sat Feb 16 00:16:49 2013
  252.                 if ( $dt -match ".{3} .{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}" ) {
  253.                         $dt=$dt.split(" ")
  254.                 $dt=$dt[2]+"/"+$dt[1]+"/"+$dt[4],$dt[3]
  255.                         $dt -join " " | Out-Null
  256.                 }
  257.                 if ( $dt -as [DateTime] ) {
  258.                 return(get-date $dt -format "dd/MM/yyy HH:mm:ss")
  259.                 } else {
  260.                         return $null
  261.                 }
  262.         }
  263.  
  264.         function Unpack-Params{
  265.             <#
  266.             .SYNOPSIS
  267.                     Unpacks file count bloc in the format
  268.                      Dirs :      1827         0      1827         0         0         0
  269.                     Files :      9791         0      9791         0         0         0
  270.                     Bytes :  165.24 m         0  165.24 m         0         0         0
  271.                     Times :   1:11:23   0:00:00                       0:00:00   1:11:23
  272.                     Parameter name already removed
  273.             .EXAMPLE
  274.                 >UnBodge-Date -dt "Sat Feb 16 00:16:49 2013"
  275.                 Returns 16-02-2013 00:16:49 in Locale format
  276.             .PARAMETER params
  277.                
  278.             #>
  279.             Param(
  280.                 $params
  281.             )
  282.  
  283.                 if ( $params.length -ge 58 ) {
  284.                         $params = $params.ToCharArray()
  285.                         $result=(0..5)
  286.                         for ( $i = 0; $i -le 5; $i++ ) {
  287.                                 $result[$i]=$($params[$($i*10 + 1) .. $($i*10 + 9)] -join "").trim()
  288.                         }
  289.                         #$result=$result -join ","
  290.                 $result=$result -join $Delim
  291.                 } else {
  292.                         #$result = ",,,,,"
  293.                 $result=$Delim+$Delim+$Delim+$Delim+$Delim
  294.                 }
  295.                 return $result
  296.         }
  297.     #endregion        
  298.     } #-- end of Begin
  299.    
  300.     Process{
  301.     $sourcecount = 0
  302.         $targetcount = 1
  303.  
  304.         #region construct the header line of the CSV
  305.     $writer.Write("File")
  306.     $fields="File"
  307.     foreach ( $HeaderParam in $HeaderParams.GetEnumerator() | Sort-Object Name ) {
  308.             if ( $HeaderParam.value -eq "counts" ) {
  309.             $tmp="~ Total"+$Delim+"~ Copied"+$Delim+"~ Skipped"+$Delim+"~ Mismatch"+$Delim+"~ Failed"+$Delim+"~ Extras"
  310.             if ((Remove-Key $headerparam.name) -match "Bytes") {
  311.                 #-- if column header is a Bytes Column then match it to the unitsize
  312.                 if ($UnitSize -and $UnitSize -ne "B") {
  313.                     #-- change the Bytes header according to the unitsize, Unitsize is GB ==> header is GBytes
  314.                             $tmp=$tmp.replace("~",$UnitSize.Substring(0,1)+ "$(Remove-Key $headerparam.name)")
  315.                 } else {
  316.                     $tmp=$tmp.replace("~","$(Remove-Key $headerparam.name)")
  317.                 }
  318.             } else {
  319.                         $tmp=$tmp.replace("~","$(Remove-Key $headerparam.name)")
  320.             }
  321.             $fields=$fields+$Delim+"$($tmp)"
  322.                     $writer.write($Delim+"$($tmp)")
  323.             } else {
  324.             $writer.write($Delim+"$(Remove-Key $HeaderParam.name)")
  325.             $fields=$fields+$Delim+"$(Remove-Key $HeaderParam.name)"
  326.             }
  327.     }
  328.  
  329.     if($fp){
  330.         $writer.write($Delim+"Scanned"+$Delim+"Newest"+$Delim+"Oldest")
  331.         $fields=$fields+$Delim+"Scanned"+$Delim+"Newest"+$Delim+"Oldest"
  332.         foreach ($fileTag in $filetags.GetEnumerator() | Sort-Object Name ) {
  333.             $writer.write($Delim+$filetag.name.split("|")[1])
  334.              $fields=$fields+$delim+$filetag.name.split("|")[1]
  335.                 }
  336.         foreach ($DirTag in $Dirtags.GetEnumerator() | Sort-Object Name ) {
  337.             $writer.write($Delim+"DIR "+$DirTag.name.split("|")[1])
  338.              $fields=$fields+$delim+"DIR "+$DirTag.name.split("|")[1]
  339.         }
  340.     }
  341.      # EOL
  342.     $writer.WriteLine()
  343.     #endregion
  344.  
  345.         $table=$fields
  346.         $filecount=0
  347.  
  348.         # Enumerate the files
  349.         foreach ($file in $files) {  
  350.                 $filecount++
  351.             write-host "$filecount/$($files.count) $($file.name) ($($file.length) bytes)"
  352.                 $results=@{}
  353.             #-- open the file
  354.                 $Stream = $file.Open([System.IO.FileMode]::Open,
  355.                            [System.IO.FileAccess]::Read,
  356.                             [System.IO.FileShare]::ReadWrite)
  357.                 $reader = New-Object System.IO.StreamReader($Stream)
  358.  
  359.                 $HeaderFooter = Get-Top $reader 16
  360.  
  361.                 if ( $HeaderFooter -match "ROBOCOPY     ::     Robust File Copy for Windows" ) {
  362.                 #-- file has valid header
  363.                         if ( $HeaderFooter -match "Files : " ) {
  364.                                 $HeaderFooter = $HeaderFooter -notmatch "Files : "
  365.                         }
  366.  
  367.                         [long]$ReaderEndHeader=$reader.BaseStream.position
  368.            
  369.                         $Footer = Get-Tail $reader 16
  370.                 #check footer of file
  371.                         $ErrorFooter = $Footer -match "ERROR \d \(0x000000\d\d\) Accessing Source Directory"
  372.                         if ($ErrorFooter) {
  373.                                 $ProcessCounts["Error"]++
  374.                                 write-host -foregroundcolor red "`t $ErrorFooter"
  375.                         } elseif ( $footer -match "---------------" ) {
  376.                                 $ProcessCounts["Processed"]++
  377.                                 $i=$Footer.count
  378.                                 while ( !($Footer[$i] -like "*----------------------*") -or $i -lt 1 ) { $i-- }
  379.                                 $Footer=$Footer[$i..$Footer.Count]
  380.                                 $HeaderFooter+=$Footer
  381.                         } else {
  382.                                 $ProcessCounts["Incomplete"]++
  383.                                 #write-host -foregroundcolor yellow "`t Log file $file is missing the footer and may be incomplete"
  384.                     write-warning "`t Log file $file is missing the footer and may be incomplete"
  385.                         }
  386.  
  387.                         foreach ( $HeaderParam in $headerparams.GetEnumerator() | Sort-Object Name ) {
  388.                                 $name = "$(Remove-Key $HeaderParam.Name)"
  389.                                 $tmp = Get-Value $($HeaderFooter -match "$name : ") $name
  390.                                 if ( $tmp -ne "" -and $tmp -ne $null ) {
  391.                                         switch ( $HeaderParam.value ) {
  392.                                                 "date" { $results[$name]=UnBodge-Date $tmp.trim() }
  393.                                                 "counts" { $results[$name]=Unpack-Params $tmp }
  394.                                                 "string" { $results[$name] = """$($tmp.trim())""" }            
  395.                                                 default { $results[$name] = $tmp.trim() }              
  396.                                         }
  397.                                 }
  398.                     #-- convert bytes statistics to numbers
  399.                     if ($name -eq "Bytes" -and ($results[$name] -ne $null)) {
  400.                         $tmp=@()    
  401.                         $results[$name].split($Delim) | % {
  402.                             #-- convert value to MBytes
  403.                             $Bytes=$_
  404.                             if ($Bytes -match "m|g|t|k") {
  405.                                 switch ($Bytes.split(" ")[1]) {                
  406.                                     "m" {$conv=1MB*$Bytes.replace(" m","MB")/1MB}
  407.                                     "k" {$conv=1KB*$Bytes.replace(" k","KB")/1KB}
  408.                                     "g" {$conv=1GB*$Bytes.replace(" g","GB")/1GB}
  409.                                     "t" {$conv=1TB*$Bytes.replace(" t","TB")/1TB}
  410.                                 }
  411.                             } else {
  412.                                 #-- copy original value, no unit sign detected
  413.                                 $conv = $Bytes
  414.                             }
  415.                             #-- convert size
  416.                             switch ($UnitSize) {
  417.                                 "MB" {$tmp+=($conv/1MB)}
  418.                                 "KB" {$tmp+=($conv/1KB)}
  419.                                 "GB" {$tmp+=($conv/1GB)}
  420.                                 "TB" {$tmp+=($conv/1TB)}
  421.                                 default {$tmp+=($conv)} #-- no conversion needed. Size is in Bytes
  422.                             }              
  423.                         }
  424.                         #-- rebuild string
  425.                         $results[$name]=$tmp -join $delim                    
  426.                     } #-- end of converting bytes statistics          
  427.                         } #-- end of parsing headerparam fields
  428.  
  429.  
  430.                         if ( $fp ) {
  431.                     #-- parse the complete file
  432.                                 write-host "Parsing $($reader.BaseStream.Length) bytes" -NoNewLine
  433.  
  434.                                 # Now go through the file line by line
  435.                     $FT_counters=@{}
  436.                     $DT_counters=@{}
  437.                     $FileTags.GetEnumerator() | select name | %{ $FT_counters.add($_.name.split("|")[1],0)}
  438.                     $DirTags.GetEnumerator() | select name | %{ $DT_counters.add($_.name.split("|")[1],0)}
  439.                                 $reader.BaseStream.Position=0
  440.                                 $filesdone = $false
  441.                                 $linenumber=0
  442.                                 $FileResults=@{}
  443.                                 $newest=[datetime]"1/1/1900"
  444.                     $oldest=Get-Date
  445.                                 $linecount++
  446.                                 $firsttick=$elapsedtime.elapsed.TotalSeconds
  447.                                 $tick=$firsttick+$refreshrate
  448.                                 $LastLineLength=1
  449.  
  450.                                 try {
  451.                                         do {
  452.                                                 $line = $reader.ReadLine()
  453.                                                 $linenumber++
  454.                                                 if (($line -eq "-------------------------------------------------------------------------------" -and $linenumber -gt 16)  ) {
  455.                                                         # line is end of job
  456.                                                         $filesdone=$true
  457.                                                 } elseif ($linenumber -gt 16 -and $line -gt "" ) {
  458.                                 #-- split line according to TAB
  459.                                                         $buckets=$line.split($tab)
  460.  
  461.                                                         if ( $buckets.count -gt 3 ) {
  462.                                     #-- line contains file information
  463.                                                                 $status=$buckets[1].trim()
  464.                                                                 $FileResults["$status"]++
  465.                                     #-- File tag counters
  466.                                     if ($status -ceq "Newer") { $FT_counters["$status" +" XN"]++ }
  467.                                     elseif ($status -ceq "Older") {  $FT_counters["$status" +" XO"]++ }
  468.                                     elseif ($status -ceq "Changed") {  $FT_counters["$status" +" XC"]++ }
  469.                                     elseif ($status -ceq "same") {  $FT_counters["$status" +" IS"]++ }
  470.                                     else {$FT_counters["$status"]++}
  471.  
  472.                                     #-- Get Timestamp from file
  473.                                                                 $SizeDateTime=$buckets[3].trim()
  474.                                                                 if ($sizedatetime.length -gt 19 ) {
  475.                                                                         $DateTime = $sizedatetime.substring($sizedatetime.length -19)
  476.                                                                         if ( $DateTime -as [DateTime] ){
  477.                                                                                 $DateTimeValue=[datetime]$DateTime
  478.                                                                                 if ( $DateTimeValue -gt $newest ) { $newest = $DateTimeValue }
  479.                                                                                 if ( $DateTimeValue -lt $oldest ) { $oldest = $DateTimeValue }
  480.                                                                         }
  481.                                                                 }
  482.                                                         } elseif ($buckets.count -eq 3) {
  483.                                     #-- line contains directory information
  484.                                     #-- Directory tag counters
  485.                                     $status=$buckets[1].Substring(0,10).trim()
  486.                                     if ($status.length -gt 0){
  487.                                         $DT_counters["$status"]++
  488.                                     } else {
  489.                                         $DT_counters["Exist"]++
  490.                                     }
  491.                                 }
  492.                                                 }
  493.  
  494.                             #-- progress indicator
  495.                                                 if ( $elapsedtime.elapsed.TotalSeconds -gt $tick ) {
  496.                                                         $line=$line.Trim()
  497.                                                         if ( $line.Length -gt 48 ) {
  498.                                                                 $line="[...]"+$line.substring($line.Length-48)
  499.                                                         }
  500.                                                         $line="$([char]13)Parsing > $($linenumber) ($(($reader.BaseStream.Position/$reader.BaseStream.length).tostring("P1"))) - $line"
  501.                                                         write-host $line.PadRight($LastLineLength) -NoNewLine
  502.                                                         $LastLineLength = $line.length
  503.                                                         $tick=$tick+$refreshrate                                               
  504.                                                 }
  505.  
  506.                                         } until ($filesdone -or $reader.endofstream)
  507.                                 }
  508.                                 finally {
  509.                                         $reader.Close()
  510.                                 }
  511.  
  512.                                 $line=$($([string][char]13)).padright($lastlinelength)+$([char]13)
  513.                                 write-host $line -NoNewLine
  514.                         }
  515.  
  516.                 #-- write results
  517.                         $writer.Write("`"$file`"")
  518.                 $line="`"$file`""
  519.                 #-- write values
  520.                         foreach ( $HeaderParam in $HeaderParams.GetEnumerator() | Sort-Object Name ) {
  521.                                 $name = "$(Remove-Key $HeaderParam.Name)"
  522.                                 if ( $results[$name] ) {
  523.                         $writer.Write("$Delim$($results[$name])")
  524.                         $line=$line+"$Delim$($results[$name])"
  525.                                 } else {
  526.                                         if ( $ErrorFooter ) {
  527.                                                 #-- placeholder
  528.                                         } elseif ( $HeaderParam.Value -eq "counts" ) {
  529.                             #-- write summary counters
  530.                             $writer.Write($Delim+$Delim+$Delim+$Delim+$Delim+$Delim)
  531.                             $line=$line+"$Delim$($results[$name])"
  532.                                         } else {
  533.                                                 $writer.Write($Delim)
  534.                             $line=$line+"$Delim$($results[$name])"
  535.                                         }
  536.                                 }
  537.                         }
  538.  
  539.                         if ( $ErrorFooter ) {
  540.                                 $tmp = $($ErrorFooter -join "").substring(20)
  541.                     $tmp=$tmp.substring(0,$tmp.indexof(")")+1)+$Delim+$tmp
  542.                                 $writer.write($delim+$delim+"$tmp")
  543.                     $line=$line+"$Delim$($results[$name])"
  544.                         } elseif ( $fp ) {
  545.                     #-- write values from file parsing         
  546.                                 $writer.write($delim+"$LineCount"+$delim+"$($newest.ToString('dd/MM/yyyy hh:mm:ss'))"+$delim+"$($oldest.ToString('dd/MM/yyyy hh:mm:ss'))")             
  547.                     $line=$line+"$Delim$($results[$name])"
  548.                     #-- write File tag counters
  549.                     foreach ($fileTag in $filetags.GetEnumerator() | Sort-Object Name ) {
  550.                     $writer.write($delim+"$($FT_counters[$Filetag.name.split("|")[1]])")
  551.                     $line=$line+$delim+$($FT_counters[$Filetag.name.split("|")[1]])
  552.                     }
  553.                     #-- write directory tag counters
  554.                     foreach ($DirTag in $DirTags.GetEnumerator() | Sort-Object Name ) {
  555.                     $writer.write($delim+"$($DT_counters[$dirtag.name.split("|")[1]])")
  556.                     $line=$line+$delim+$($dT_counters[$DirTag.name.split("|")[1]])
  557.                     }
  558.                         }
  559.                 #-- EOL
  560.                         $writer.WriteLine()
  561.                 $table=$table+"`n"+$line
  562.                 } else {
  563.                 #-- file is not a RoboCopy log file
  564.                         #write-host -foregroundcolor darkgray "$($file.name) is not recognised as a RoboCopy log file"
  565.                 write-warning "$($file.name) is not recognised as a RoboCopy log file"
  566.                 }
  567.         }
  568.     } #- -end of Process
  569.  
  570.     End{
  571.         #-- write and output results
  572.         write-host "$filecount files scanned in $($elapsedtime.elapsed.tostring()), $($ProcessCounts["Processed"]) complete, $($ProcessCounts["Error"]) have errors, $($ProcessCounts["Incomplete"]) incomplete"
  573.         write-host  "Results written to $($writer.basestream.name)"
  574.         $CSVFile=$writer.basestream.name
  575.         $writer.close() #-- yes, we are done writing
  576.         #-- create output object, containing name of CSV file and Report object
  577.         $Output=New-Object -TypeName psobject -Property @{ReportFileName=$CSVFile
  578.                                                           Report=ConvertFrom-Csv -Delimiter $Delim -InputObject $table
  579.                                                           Summary=@()}
  580.  
  581.         #-- summize, build a Robocopy like Summary
  582.         $Types=@("Dirs","Files",("Bytes".replace("B",$SizeUnit)),"Times","Speed")
  583.         $types | % {
  584.             $row= "" | select  Type,Total,Copied,Skipped,Mismatch,FAILED,Extras
  585.             $row.type=$_
  586.             if ($row.type -ilike "Times") {
  587.                 #-- calculate times
  588.                 $Output.Report | %{
  589.                     if ($_.ended.length -gt 0){
  590.                         #-- only use data from complete RC logs
  591.                         $row.total=$row.total+[timespan]$_."Times Total"
  592.                         $row.Failed=$row.Failed+[timespan]$_."Times Failed"
  593.                         $row.Copied=$row.Copied+[timespan]$_."Times Copied"
  594.                         $row.Extras=$row.Extras+[timespan]$_."Times Extras"  
  595.                     } else {
  596.                         Write-Verbose ("RC log "+$_.file+" skipped for summary calculation. Log file is not complete.")
  597.                     }    
  598.                 }
  599.             } elseif ($row.type -ilike "Speed") {
  600.                 #-- Calculate speed
  601.                 $Duration=0
  602.                 $output.Report | %{
  603.                         if ($_.ended.length -gt 0){
  604.                             #-- only use data from complete RC logs
  605.                             $Duration=$Duration+[timespan]$_."Times Copied"
  606.                         }
  607.                     }
  608.                 $row.Copied="{0:N1}" -F ((($output.report | Measure-Object -Property (("Bytes".replace("B",$SizeUnit))+" Copied") -Sum).sum) / $Duration.seconds)
  609.             } else {
  610.                 #-calculate counters
  611.                 $row.Total="{0:N1}" -f ($output.report | Measure-Object -Property ($row.type+" Total") -Sum).sum
  612.                 $row.Copied="{0:N1}" -f($output.report | Measure-Object -Property ($row.type+" Copied") -Sum).sum
  613.                 $row.Skipped="{0:N1}" -f($output.report | Measure-Object -Property ($row.type+" Skipped") -Sum).sum
  614.                 $row.Mismatch="{0:N1}" -f($output.report | Measure-Object -Property ($row.type+" Mismatch") -Sum).sum
  615.                 $row.Failed="{0:N1}" -f($output.report | Measure-Object -Property ($row.type+" Failed") -Sum).sum
  616.                 $row.Extras="{0:N1}" -f($output.report | Measure-Object -Property ($row.type+" Extras") -Sum).sum
  617.             }
  618.             $Output.Summary+=$row
  619.         }
  620.         [System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture
  621.         Write-Verbose ("Changed Locale back to "+$oldCulture.Name)
  622.         return($output) #-- done, fini, finaly....
  623.     } #-- end of End
  624. }

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me