# encoding: ascii # api: powershell # title: RoboCopyWrapper # description: PowerShell RoboCopy Wrapper example code # version: 0.1 # type: script # license: CC0 # x-poshcode-id: 603 # x-archived: 2008-09-26T19:10:19 # # # Robocopy example code for more info see the series on my blog # http://thepowershellguy.com/blogs/posh/archive/tags/robocopy/default.aspx ############################################################################################# ## Make RoboCopy Help Object ############################################################################################# $RoboHelp = robocopy /? | Select-String '::' $r = [regex]'(.*)::(.*)' $RoboHelpObject = $RoboHelp | select ` @{Name='Parameter';Expression={ $r.Match( $_).groups[1].value.trim()}}, @{Name='Description';Expression={ $r.Match( $_).groups[2].value.trim()}} $RoboHelpObject = $RoboHelpObject |% {$Cat = 'General'} { if ($_.parameter -eq '') { if ($_.Description -ne ''){ $cat = $_.description -replace 'options :',''} } else { $_ | select @{Name='Category';Expression={$cat}},parameter,description } } ############################################################################################# ## Robocopy example command : ############################################################################################# robocopy 'c:\test1' c:\PowerShellRoboTest /r:2 /w:5 /s /v /np | Tee-Object -Variable RoboLog ############################################################################################# ## Process the Output ############################################################################################# $null,$StartBegin,$StartEnd,$StopBegin = $RoboLog | Select-String "----" |% {$_.linenumber} $RoboStatus = New-Object object # Start information $robolog[$StartBegin..$StartEnd] | % { Switch -regex ($_) { 'Started :(.*)' { Add-Member -InputObject $RoboStatus -Name StartTime ` -Value ([datetime]::ParseExact($matches[1].trim(),'ddd MMM dd HH:mm:ss yyyy',$null)) ` -MemberType NoteProperty } 'Source :(.*)' { Add-Member -InputObject $RoboStatus -Name Source ` -Value ($matches[1].trim()) -MemberType NoteProperty } 'Dest :(.*)' { Add-Member -InputObject $RoboStatus -Name Destination ` -Value ($matches[1].trim()) -MemberType NoteProperty } 'Files :(.*)' { Add-Member -InputObject $RoboStatus -Name FileName ` -Value ($matches[1].trim()) -MemberType NoteProperty } 'Options :(.*)' { Add-Member -InputObject $RoboStatus -Name Options ` -Value ($matches[1].trim()) -MemberType NoteProperty } } } # Stop Information $robolog[$StopBegin..( $RoboLog.Count -1)] |% { Switch -regex ($_) { 'Ended :(.*)' { Add-Member -InputObject $RoboStatus -Name StopTime ` -Value ([datetime]::ParseExact($matches[1].trim(),'ddd MMM dd HH:mm:ss yyyy',$null))` -MemberType NoteProperty } 'Speed :(.*) Bytes' { Add-Member -InputObject $RoboStatus -Name BytesSecond ` -Value ($matches[1].trim()) -MemberType NoteProperty } 'Speed :(.*)MegaBytes' { Add-Member -InputObject $RoboStatus -Name MegaBytesMinute ` -Value ($matches[1].trim()) -MemberType NoteProperty } '(Total.*)' { $cols = $_.Split() |? {$_} } 'Dirs :(.*)' { $fields = $matches[1].Split() |? {$_} $dirs = new-object object 0..5 |% { Add-Member -InputObject $Dirs -Name $cols[$_] -Value $fields[$_] -MemberType NoteProperty Add-Member -InputObject $Dirs -Name 'toString' -MemberType ScriptMethod ` -Value {[string]::Join(" ",($this.psobject.Properties | % {"$($_.name):$($_.value)"}))} -force } Add-Member -InputObject $RoboStatus -Name Directories -Value $dirs -MemberType NoteProperty } 'Files :(.*)' { $fields = $matches[1].Split() |? {$_} $Files = new-object object 0..5 |% { Add-Member -InputObject $Files -Name $cols[$_] -Value $fields[$_] -MemberType NoteProperty Add-Member -InputObject $Files -Name 'toString' -MemberType ScriptMethod -Value ` {[string]::Join(" ",($this.psobject.Properties |% {"$($_.name):$($_.value)"}))} -force } Add-Member -InputObject $RoboStatus -Name files -Value $files -MemberType NoteProperty } 'Bytes :(.*)' { $fields = $matches[1].Split() |? {$_} $fields = $fields |% {$new=@();$i = 0 } { if ($_ -match '\d') {$new += $_;$i++} else {$new[$i-1] = ([double]$new[$i-1]) * "1${_}B" } }{$new} $Bytes = new-object object 0..5 |% { Add-Member -InputObject $Bytes -Name $cols[$_] ` -Value $fields[$_] -MemberType NoteProperty Add-Member -InputObject $Bytes -Name 'toString' -MemberType ScriptMethod ` -Value {[string]::Join(" ",($this.psobject.Properties | % {"$($_.name):$($_.value)"}))} -force } Add-Member -InputObject $RoboStatus -Name bytes -Value $bytes -MemberType NoteProperty } } } # Process the details log $re = New-Object regex('(.*)\s{2}([\d\.]*\s{0,1}\w{0,1})\s(.*)') $RoboDetails = $robolog[($StartEnd +1)..($stopbegin -3)] |? {$_.StartsWith([char]9)} | select ` @{Name='Action';Expression={$re.Match($_) |% {$_.groups[1].value.trim()}}}, @{Name='Size';Expression={$re.Match($_) |% {$_.groups[2] |% {$_.value.trim()}}}}, @{Name='Directory';Expression={if(!($re.Match($_) |% {$_.groups[1].value.trim()})){ '-';$Script:dir = $re.Match($_) |% {$_.groups[3] | % {$_.value.trim()}} }else {$script:dir}}}, @{Name='Name';Expression={$re.Match($_) |% {$_.groups[3] |% {$_.value.trim()}}}} # convert all values to bytes (but is also possible switch on robocopy ) 0..($RoboDetails.count -1) |% { if ($Robodetails[$_].Directory -eq '-') { $Robodetails[$_].Action = 'Directory' $Robodetails[$_].Directory = split-path $Robodetails[$_].Name } if ($Robodetails[$_].size -match '[mg]') { $Robodetails[$_].size = [double]($roboDetails[$_].size.trim('mg ')) * 1mb } } #Add-Member -InputObject $RoboDetails -Name 'toString' -MemberType ScriptMethod ` -Value {"Details : " + $this.count} -force Add-Member -InputObject $RoboStatus -Name Details ` -Value $RoboDetails -MemberType NoteProperty # Process warnings and errors $reWarning = New-Object regex('(.*)(ERROR.*)(\(.*\))(.*)\n(.*)') $roboWarnings = $reWarning.matches(($robolog | out-string)) | Select ` @{Name='Time';Expression={[datetime]$_.groups[1].value.trim()}}, @{Name='Error';Expression={$_.groups[2].value.trim()}}, @{Name='Code';Expression={$_.groups[3].value.trim()}}, @{Name='Message';Expression={$_.groups[5].value.trim()}}, @{Name='Info';Expression={$_.groups[4].value.trim()}} #Add-Member -InputObject $RoboWarnigs -Name 'toString' ` -MemberType ScriptMethod -Value {"Details : " + $this.count} -force Add-Member -InputObject $RoboStatus -Name Warnings ` -Value $roboWarnings -MemberType NoteProperty $reErrors = New-Object regex('\) (.*)\n(.*)\nERROR:(.*)') $roboErrors = $reErrors.matches(($robolog |? {$_}| out-string)) | Select ` @{Name='Error';Expression={$_.groups[3].value.trim()}}, @{Name='Message';Expression={$_.groups[2].value.trim()}}, @{Name='Info';Expression={$_.groups[1].value.trim()}} #Add-Member -InputObject $RoboErrors -Name 'toString' ` -MemberType ScriptMethod -Value {"Details : " + $this.count} -force Add-Member -InputObject $RoboStatus -Name Errors ` -Value $RoboErrors -MemberType NoteProperty ############################################################################################# ## Use $roboStatus Object created to get and format the statistics : ############################################################################################# # check status $RoboStatus # Calculate time running "Time elapsed : $($RoboStatus.StopTime - $RoboStatus.StartTime)" # Get Help for Options given $RoboStatus.Options.split()[1..100] | % { $par = $_ ;$RoboHelpObject |? {$_.parameter -eq $par} } | ft -a # Details on files and directories (to string overruled!) : $RoboStatus.files $RoboStatus.Directories # Group Details $RoboStatus.Details | group action # List Errors and Warnings $RoboStatus.Errors | fl $RoboStatus.Warnings # Get count of warnings $RoboStatus.Warnings |group info | ft count,Name -a # Only warnings that resoved in a failed copy $RoboStatus.Warnings | select *, @{name='Failed';e={($RoboStatus.errors |% {$_.info}) -contains $_.info}} # Action Details with warnings $RoboStatus.details |? {$_.action} | select *, @{name='Failed';e={$d = $_;($RoboStatus.errors | % {$_.info}) -match '\\'+([regex]::escape($d.name))}} |? {$_.failed} | group action,directory,name | ft -a name,count # Count of warnings per error $RoboStatus.Errors | select *,@{name='Warnings';e={$e = $_;($robostatus.warnings |? {$_.info -eq $e.info}).count}} # List of Warnings per error $RoboStatus.Errors | select *,@{name='Warnings';e={$e = $_;($robostatus.warnings |? {$_.info -eq $e.info})}} |% { $_ | fl error,Info ;$_.warnings | sort -u info,message | ft [tecm]* -a }