8

I have a quick script written to compare file differences across servers, and then give a prompt for whether to mirror the changes. I'd like it to intelligently NOT prompt for copying if there aren't any changes.

The script:

robocopy "$cSource" "$cDestination" /E /L /FP

$cDecision = Read-host "Continue = 1, anything else = Stop"

if ($cDecision -eq "1") {
robocopy "$cSource" "$cDestination" /MIR /FFT /Z /W:5 /MT:64 /XX /log:$cLogLocation

Invoke-item $cLogLocation

}

else { Write-host "Quit!" }

I'd like to have Powershell not ask to continue if Robocopy reports 0 for Mismatch, Failed, Extras, and Copied. It's not very important for this particular project, but I can think of a couple useful uses for such a check. Thanks!

jski
  • 911
  • 1
  • 7
  • 20

3 Answers3

7

Finally something people didn't answer in 10 seconds! I used your issue to continue to learn more about Powershell. The following works for me, I hope to see how others will slim down this code:

Clear-Host
$ErrorActionPreference = "Continue"
$DebugPreference = "Continue"
$VerbosePreference = "Continue"

@"

## robocopy_helper.ps1 ########################################################
Usage:        powershell -ExecutionPolicy Bypass -File ./robocopy_helper.ps1

Purpose:      Dry run before Full run of robocopy

History:      07/11/2014  -  Created
###############################################################################

"@

## User Supplied Variables
$cSrc = "C:\Temp"
$cDst = "C:\Temp2"
$cLog = "c:\robo.log"

## Robocopy Dry Run
$robo_test = robocopy "$cSrc" "$cDst" /E /L /FP

## Use Regular Expression to grab the following Table
#               Total    Copied   Skipped  Mismatch    FAILED    Extras
#    Dirs :         1         0         1         0         0         0
#   Files :         1         0         1         0         0         0
$robo_results = $robo_test -match '^(?= *?\b(Total|Dirs|Files)\b)((?!    Files).)*$'

## Convert Table above into an array
$robo_arr = @()
foreach ($line in $robo_results){
    $robo_arr += $line
}

## Create Powershell object to tally Robocopy results
$row = "" |select COPIED, MISMATCH, FAILED, EXTRAS
$row.COPIED = [int](($robo_arr[1] -split "\s+")[4]) + [int](($robo_arr[2] -split "\s+")[4])
$row.MISMATCH = [int](($robo_arr[1] -split "\s+")[6]) + [int](($robo_arr[2] -split "\s+")[6])
$row.FAILED = [int](($robo_arr[1] -split "\s+")[7]) + [int](($robo_arr[2] -split "\s+")[7])
$row.EXTRAS = [int](($robo_arr[1] -split "\s+")[8]) + [int](($robo_arr[2] -split "\s+")[8])

## If there are differences, lets kick off robocopy again
if ( ($row.COPIED + $row.MISMATCH + $row.FAILED + $row.EXTRAS) -gt 0 ){
    robocopy "$cSrc" "$cDst" /MIR /FFT /Z /W:5 /MT:64 /XX /log:$cLog
    Invoke-item $cLog
}
else { Write-host "Folders '$cSrc' and '$cDst' are twins" }

Note: I have changed the regular expression above from a period to a space immediately before the asterisk. This eliminates the risk of catching 'files' or 'dirs' or 'totals' in a filename/path. The original line is below for posterity.
jski 2014/07/16

$robo_results = $robo_test -match '^(?=.?\b(Total|Dirs|Files)\b)((?! Files).)$'

Lars
  • 198
  • 1
  • 10
  • 1
    What is a more efficient way to grab the integers from the Robocopy dry run output? Manipulating the same data multiple times bothered me, but I didn't know another way to do it. Thanks, – Lars Jul 14 '14 at 13:35
  • Edited your post to avoid a possible error with certain filenames. – jski Jul 16 '14 at 20:26
  • awesome you saved me a lot of time :-) – JoeRod Sep 16 '21 at 18:24
2

I realize that this is an older thread, but note that this can also be accomplished using the exit code returned by robocopy to determine whether any changes exist (a value of 0 indicates no changes). There are 2 ways to do this:

  1. Run your robocopy statement above, and then in the next line, check the value of $LastExitCode.
  2. Use Start-Process:
$RobocopyResult = Start-Process -FilePath robocopy -ArgumentList $RobocopyParams -Wait -PassThru
$RobocopyResult.ExitCode

If using Start-Process, it is important to use -PassThru, otherwise no value will be set in $RobocopyResult.

Other exit codes are listed at http://ss64.com/nt/robocopy-exit.html.

Aakash Shah
  • 103
  • 1
  • 5
0

Here's my hack at this. My code reads the logs file and looks for the rows that match the summary fields. It then parses it into two objects that contain the stats for files and for directories:

        ### Read data from log summary at end of RoboCopy log
        $Props = ((cat $RoboCopyLogFile | ? {$_ -like "*Total*Copied*Skipped*Mismatch*FAILED*Extras*"}) | select -Last 1).split(" ") | ? {$_ -match "a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"}
        $Dirs = (((cat $RoboCopyLogFile | ? {$_ -like "*Dirs : *"})) | select -Last 1).split(" ") | ? {$_ -match "1|2|3|4|5|6|7|8|9|0"}
        $Files = (((cat $RoboCopyLogFile | ? {$_ -like "*Files : *"})) | select -Last 1).split(" ") | ? {$_ -match "1|2|3|4|5|6|7|8|9|0"}
        ### Loop through the propteries of the summary
        $i = 0
        $FileStats = @{}
        $DirStats = @{}
        foreach($Prop in $Props)
        {
            $FileStats.("$Prop") = $Files[$i]
            $DirStats.("$Prop") = $Dirs[$i]
            $i++
        }
        $FileStats = New-Object -TypeName psobject -Property $FileStats
        $DirStats = New-Object -TypeName psobject -Property $DirStats
Jason
  • 1