PowerShell and Robocopy part 3
In this 3rd part of this series we go on with the output of a Test copy, and will start to parse the robocopy log, I just made a sTest1 directory with some test files and started the command as explained in last post,
You can see in the output that there are 3 distinct parts in the Robocopy output, Start, Details, Stop information divided by a line, so the first thing is dividing the output of robocopy in the 3 parts and process them separately, we do that by this line :
$null,$StartBegin,$StartEnd,$StopBegin = $RoboLog | Select-String "----" |% {$_.linenumber}
as you see in the output below this gives us the line numbers of lines separating the different parts :
After that I make a new object and start parsing the Start information, adding the information to the $RobocopyStatus object,
note that the Date get's translated from text into a "Real" Date-time object (might need adapting for other cultures as en-us)
-Value ([datetime]::ParseExact($matches[1].trim(),'ddd MMM dd HH:mm:ss yyyy',$null)) `
The Rest of the code for the start part of the Robocopy log :
#############################################################################################
## 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
}
}
}
This results in a $robostatus object that we can check the start information on like this :
and as the StartTime is a Object also we can for example also select the Year only.
Next thing to do is and the End information also :
# 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
}
}
}
Now it already get's more interesting as we can do things like this now :
You can see above that we can calculate the running time now and see the count of files, be sure to take a look at the code to parse the Stop information, as you can find some nifty tricks as overriding the ToString() method on the Files and Directory collections to make them more clear in default output:
Add-Member -InputObject $Files -Name 'toString' -MemberType ScriptMethod -Value ` {[string]::Join(" ",($this.psobject.Properties |% {"$($_.name):$($_.value)"}))} -force
In the next part we will do some more text parsing as we're also going to process the Detail information from the robocopy log .
Enjoy,
Greetings /\/\o\/\/