storme Posted October 30, 2010 Posted October 30, 2010 (edited) BACKGROUND (for those that haven’t used Robocopy)I ran into Robocopy some time ago and it’s an amazing command line with a heap of options to do almost anything you want. Technet Robocopy command line referenceIt originally came with the Windows Server 2003 Resource Kit Tools as version XP010.A newer version XP026 was released with the Robocopy GUI.The latest version XP027 is part of Vista and Windows 7 (not compatible with Windows XP).The Vista/7 version is even Multithreaded.I found a nice tool Easy RoboCopy that helps with the command line and could be interesting if you want to use robocopy in batch file.But I’d like to use Robocopy in my programs without having to re-create the command line every time. So I’ve created this function to make it simple to use robocopy and to set the switches how I want them to be.Have a look at the script and I’d appreciate feedback. It’s still a work in progress adn has a lot of consolewrite commands but it does what I want now and I know that is when I tend to go cold on a project and never finish it...sigh... so if I get some feedback on this it will give me some impetus to polish it.Read the header for how to use it and the test program for examples. If you have any questions please let me know. <_RoboCopy.au3>expandcollapse popup#include <Constants.au3> ; STDIN & STDOUT ;Global Const $STDIN_CHILD = 1 ;Global Const $STDOUT_CHILD = 2 ;Global Const $STDERR_CHILD = 4 ;Global Const $STDERR_MERGED = 8 ;Global Const $STDIO_INHERIT_PARENT = 0x10 ;Global Const $RUN_CREATE_NEW_CONSOLE = 0x00010000 ;=============================================================================== ; ; Function Name: _robocopy($source, $target, $stitches = "mirror", $excludeFiles = "", $excludeFolders = "", $logFile = "", $statusBox = "", $progressBar = "") ; Description:: Runs the robocopy command ; Parameter(s): $source - Source directory ; $target - Target Directory ; $stitches - Standard Robocopy switches, predefined meta commands or combination of both ; - "Meta" or "Std Switches" or [meta][Space][Std switches] (eg mirror /r:0 /w:0) ; - Mirror - Clone source to target (includeing subdirectories) ; - Add - Add new/updates files but don't delete anything ; - Copy - Same as Add ; - CopySub - Same as add but also copies all subdirectories ; - Move - Move files to destination ; - MoveSub - Same as move but also copies all subdirectories ; $includeFiles - A list of files to include (Comma delimited) ; - OR An array of files ; $excludeFiles - A list of files to exclude (Comma delimited) ; - OR An array of files ; $excludeFolders - A list of folders to exclude (Comma delimited) ; - OR An array of files ; $logFile - Filename for log file ; If File starts with + then append to log ; $outputFunction - Function name to pass text to from robocopy ; - Only full lines will be passed to this function ; - This will be run by execute command in the form {Function name}($text) ; Requirement(s): Robocopy is installed on the computer or in the same directoy as script ; Return Value(s): Success: ??? ; Error: 0 and @error = ; = 1 - Robocopy.exe can't be found ; = 2 - Source doesn't Exist ; = 3 - Destination can't be created (eg maybe read only media) ; = 4 - Log File can't be created ; = 5 - Robocopy Cant' be run ; Author(s): John Morrison ; ; Basis: ; Thanks: PsaltyDS http://www.autoitscript.com/forum/index.php?showtopic=98602&view=findpost&p=720326 _ProcessGetExitCode() ;=============================================================================== Func _robocopy($source, $target, $switches = "mirror", $includeFiles = "", $excludeFiles = "", $excludeFolders = "", $logFile = "", $outputFunction = "") #cs ConsoleWrite("All Switches" & @CR & _ "$source = <" & $source & ">" & @CR & _ "$target = <" & $target & ">" & @CR & _ "$switches = <" & $switches & ">" & @CR & _ "$includeFiles = <" & $includeFiles & ">" & @CR & _ "$excludeFolders = <" & $excludeFolders & ">" & @CR & _ "$logFile = <" & $logFile & ">" & @CR & _ "$outputFunction = <" & $outputFunction & ">" & @CR) #ce ; Check if Robocopy can be executed Local $roboCopyCMD = "" While $roboCopyCMD = "" If FileExists(@SystemDir & "\robocopy.exe") Then $roboCopyCMD = @SystemDir & "\robocopy.exe " ElseIf FileExists(@ScriptDir & "\robocopy.exe") Then $roboCopyCMD = @ScriptDir & "\robocopy.exe " ElseIf FileExists(@ScriptDir & "\getrobocopy.exe") Then ;Download Robocopy If RunWait(@ScriptDir & "\getrobocopy.exe", @TempDir) <> 0 Then Return SetError(1, "", 0) ; Can't find Robocopy EndIf Else Return SetError(1, "", 0) ; Can't find Robocopy EndIf WEnd $workingdir = $source ; Check source exists If StringRight($source, 1) = "\" Then ; remove trailing slash \ $source = StringLeft($source, StringLen($source) - 1) EndIf If Not FileExists($source) Then ; source doesn't exist Return SetError(2, "", 0) ; Source doesn't Exist EndIf $source = _RCProcessFilelist($source) ; Check Target reachable If StringRight($target, 1) = "\" Then ; remove trailing slash \ $target = StringLeft($target, StringLen($target) - 1) EndIf If Not FileExists($target) Then If Not DirCreate($target) Then Return SetError(3, "", 0) ; Destination can't be created (eg maybe read only media) EndIf EndIf $target = _RCProcessFilelist($target) ;Workout robocopy switches from Meta While 1 ;Loop until all metas are removed Select ;Case _RCcheckMeta("sub", "/E", $switches) Case _RCcheckMeta("mirror", "/MIR ", $switches) Case _RCcheckMeta("Add", "", $switches) Case _RCcheckMeta("Copy", "", $switches) Case _RCcheckMeta("CopySub", "/E", $switches) Case _RCcheckMeta("Move", "/MOVE", $switches) Case _RCcheckMeta("MoveSub", "/MOVE /E", $switches) Case Else ;MsgBox(0, "pause", "META EKSE") ExitLoop EndSelect ;MsgBox(0, "pause", "META") WEnd ;Include files If $includeFiles = "" Then $includeFiles = "*.*" $switches = _RCProcessFilelist($includeFiles) & " " & StringStripWS($switches, 1) ;Exclude files/folders If $excludeFiles <> "" Then $switches &= " /XF " & _RCProcessFilelist($excludeFiles) EndIf If $excludeFolders <> "" Then ; Add target to 'excluded files' in case tragete is inside source (eg source=c: target=c:\backup) $switches &= " /XD " & _RCProcessFilelist($excludeFolders) & " " & _RCProcessFilelist($target) EndIf ; LOG If $logFile <> "" Then If StringLeft($logFile, 1) = "+" Then $logFile = StringMid($logFile, 2, 255) $switches &= ' /log+:"' & $logFile & '"' Else $switches &= ' /log:"' & $logFile & '"' EndIf $hfile = FileOpen($logFile, 1) If $hfile = -1 Then Return SetError(4, "", 0) ;Can't append/create Log file error EndIf FileClose($hfile) EndIf ;Add extra switches if nessacary If Not StringInStr($switches, "/r:") Then ; retry count $switches &= " /r:0" EndIf If Not StringInStr($switches, "/w:") Then ; Wait time $switches &= " /w:0" EndIf If Not StringInStr($switches, "/ZB") Then ; Tries to copy files in restartable mode, but if that fails with an “Access Denied” error, switches automatically to Backup mode. ;TODO - MAY drop this out as slows copy ??? $switches &= " /ZB" EndIf If Not StringInStr($switches, "/FFT") Then ; Assume FAT File Times (2-second granularity). Useful for copying to third-party systems that declare a volume to be NTFS but only implement file times with a 2-second granularity.. $switches &= " /FFT" EndIf If Not StringInStr($switches, "/COPY:") Then ; Copies Data, Attributes, Timestamps only (SEE $switches &= " /COPY:DAT" EndIf ; Wasn't as useful as it looked- removed ;If Not StringInStr($switches, "/ETA") Then ; ; Shows estimated time of completion for copied files. ; $switches &= " /ETA" ;EndIf If StringInStr($switches, "/log") Then ; Displays output in the console window, in addition to directing it to the log file specified by /LOG or /LOG+. ; Needed for status and progress displays $switches &= " /TEE" EndIf ; DEGUB TODO - remove commenting after testing ;If $progressBar = "" Then ; ; no progress bar so "suppress the display of progress information" ; $switches &= " /NP " ;EndIf #cs MsgBox(0, "ROBOCOPY", _ "$source = <" & $source & ">" & @CR & _ "$target = <" & $target & ">" & @CR & _ "$switches = <" & $switches & ">" & @CR & _ "$roboCopyCMD = <" & $roboCopyCMD & " " & $source & " " & $target & " " & $switches & ">" & @CR & _ "") #ce ConsoleWrite($roboCopyCMD & " " & $source & " " & $target & " " & $switches & @CR) ;Exit ; run robocopy Local $robocopyPID = Run('"' & $roboCopyCMD & '" ' & $source & " " & $target & " " & $switches, $workingdir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD) If $robocopyPID = 0 Then Return SetError(5, "", 0) ; Robocopy failed to run Else Local $hrobocopy = _ProcessGetHandle($robocopyPID) ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hrobocopy = ' & $hrobocopy & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console If @error Then MsgBox(0,"Error _ProcessGetHandle", "@error = " & @error) Local $cmdOut, $cmdoutlast, $buffer, $loopCount, $buffer2, $sPosit ;Local $cmdOutErr = 1 ; so loop is run at least once While 1 ; ProcessExists($robocopyPID); Or ($cmdOutErr = 0) ; $cmdOurErr - used to wait untill ALL data is extracted from command line $cmdOut = StdoutRead($robocopyPID) ;$cmdOutErr = @error If @error Then ExitLoop If $cmdOut <> $cmdoutlast Then $cmdoutlast = $cmdOut $fnewdata = True $buffer &= $cmdOut ; Stores all data for processing ;interpret $BUFFER data ;extract and send to $statusFunc() the buffer string up to and including the last @CRLF $posit = StringInStr($buffer, @CRLF, 0, -1) + 2 If Not @error Then $buffer2 = StringStripWS(StringLeft($buffer, $posit), 2) If $buffer2 <> "" Then ;ConsoleWrite($posit & "|" & StringLen($buffer) & "<<" & $buffer2 & ">>" & Asc(StringMid($buffer, $posit, 1)) & "|" & $buffer & "|" & @CR) Execute($outputFunction & '("' & $buffer2 & '")') EndIf $buffer = StringMid($buffer, $posit, 1000) ;ConsoleWrite("<<<" & StringMid($buffer, $posit, 1000) & ">>>" & @CR) EndIf EndIf ; $cmdOut ;endif ; $outputFunction Sleep(20) WEnd Local $iReturnCode = _ProcessGetExitCode($hrobocopy) ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iReturnCode = ' & $iReturnCode & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console If @error Then MsgBox(0,"Error _ProcessGetExitCode", "@error = " & @error) #cs BITMAP Value Meaning If Set 16 Serious error. Robocopy did not copy any files. This is either a usage error or an error due to insufficient access privileges on the source or destination directories. 8 Some files or directories could not be copied (copy errors occurred and the retry limit was exceeded). Check these errors further. 4 Some Mismatched files or directories were detected. Examine the output log. Housekeeping is probably necessary. 2 Some Extra files or directories were detected. Examine the output log. Some housekeeping may be needed. 1 One or more files were copied successfully (that is, new files have arrived). 0 No errors occurred, and no copying was done. The source and destination directory trees are completely synchronized. #ce _ProcessCloseHandle($hrobocopy) If @error Then MsgBox(0,"Error _ProcessCloseHandle", "@error = " & @error) Return $iReturnCode EndIf EndFunc ;==>_robocopy Func _RCcheckMeta($meta, $metaSwitch, ByRef $switches) ;check if meta is the first part of $switches or whole of switches ; if so replace with Robocopy switches for that meta $meta = StringLower($meta) If StringInStr($switches, $meta & " ") = 1 Or StringLower($switches) = $meta Then $switches = StringStripWS(StringStripWS($metaSwitch & " " & StringStripWS(StringMid($switches, StringLen($meta) + 1, 100), 1), 1), 2) Return True Else Return False EndIf EndFunc ;==>_RCcheckMeta Func _RCProcessFilelist($fileList) ; $includeFiles = either a comma delimited string or Array of strings Local $sReturn = "" If Not IsArray($fileList) Then ;Standard string so convert to array $fileList = StringSplit($fileList, ",", 2) EndIf For $item = 0 To UBound($fileList) - 1 $fileList[$item] = StringStripWS(StringStripWS($fileList[$item], 1), 2) If StringLeft($fileList[$item], 1) <> '"' Then ;Search for inverted commas as first character $fileList[$item] = '"' & $fileList[$item] EndIf If StringRight($fileList[$item], 1) <> '"' Then ;Search for inverted commas as first character $fileList[$item] &= '"' EndIf $sReturn &= " " & $fileList[$item] Next Return StringStripWS($sReturn, 1) ; Strip leading space EndFunc ;==>_RCProcessFilelist ; Return handle of given PID Func _ProcessGetHandle($iPID) Local Const $PROCESS_QUERY_INFORMATION = 0x0400 Local $avRET = DllCall("kernel32.dll", "ptr", "OpenProcess", "int", $PROCESS_QUERY_INFORMATION, "int", 0, "int", $iPID) If @error Then Return SetError(1, 0, 0) Else Return $avRET[0] EndIf EndFunc ;==>_ProcessGetHandle ; Close process handle Func _ProcessCloseHandle($hProc) Local $avRET = DllCall("kernel32.dll", "int", "CloseHandle", "ptr", $hProc) If @error Then Return SetError(1, 0, 0) Else Return 1 EndIf EndFunc ;==>_ProcessCloseHandle ; Get process exit code from handle Func _ProcessGetExitCode($hProc) Local $t_ExitCode = DllStructCreate("int") Local $avRET = DllCall("kernel32.dll", "int", "GetExitCodeProcess", "ptr", $hProc, "ptr", DllStructGetPtr($t_ExitCode)) If @error Then Return SetError(1, 0, 0) Else Return DllStructGetData($t_ExitCode, 1) EndIf EndFunc ;==>_ProcessGetExitCodeAny feedback would be appreciated!Thank youJohn MorrisonakaStorm-EEDIT: 23/02/2011 - Fix so source can be set to "c:\" Edited February 22, 2011 by storme Some of my small contributions to AutoIt Browse for Folder Dialog - Automation SysTreeView32 | FileHippo Download and/or retrieve program information | Get installedpath from uninstall key in registry | RoboCopy function John Morrison aka Storm-E
Chimaera Posted November 1, 2010 Posted November 1, 2010 Thanks for posting this I run robocopy a lot from commandline for doing cutomer backups and was thinking about making an autoit version this will give me a push in the right direction. many thanks Chimaera If Ive just helped you ... miracles do happen. Chimaera CopyRobo() * Hidden Admin Account Enabler * Software Location From Registry * Find Display Resolution * _ChangeServices()
storme Posted February 22, 2011 Author Posted February 22, 2011 Just found a little bug. Robocopy.exe rejects a source of "C:\" but "C:" is ok. Don't ask me why that is just what it does. The big problem is if you specify "C:" as the source it will start copying from the "current" directory (IE where ever you are). The fix (OP updated) is to set the "working directory" to teh specified source and remove any trailing back slashes. Thanks Chimaera for bringing that robocopy "feature" to light so I could fix it. John Morrison AKA Storm-E Some of my small contributions to AutoIt Browse for Folder Dialog - Automation SysTreeView32 | FileHippo Download and/or retrieve program information | Get installedpath from uninstall key in registry | RoboCopy function John Morrison aka Storm-E
Gigglestick Posted May 5, 2011 Posted May 5, 2011 How about instead of limiting the location of robocopy.exe?$roboCopyCMD = _FileInPath("robocopy.exe") If $roboCopyCMD = "" Then Return SetError(1, 0, "")Also, it's SetError(@error, @extended, return value). You used SetError(0, "", 0). My UDFs: ExitCodes
smartkey Posted February 8, 2018 Posted February 8, 2018 Please refer the problem faced while using _Robocopy.au3 in below post. Looks like it has to be fine tuned or has to be corrected.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now