# 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
}