Copy only modified files
Copy only modified files
Hi,
I have a batch file that I use to xcopy several directories using the /D flag, so that only modified files are copied when compared to the destination. It works OK, but I would like to be able to do more error handling and reporting so I found PureBasic. I have the demo, and have spent all day looking at sample code and tutorials, but I can't figure out how to copy a directory recursively and only overwrite those file which have been modified. I was going to go through a foreach loop and compare the modified dates of the source and destination, but that seems like WAY too much effort!
Can anyone help out?
Thanks in advance!
tcb
I have a batch file that I use to xcopy several directories using the /D flag, so that only modified files are copied when compared to the destination. It works OK, but I would like to be able to do more error handling and reporting so I found PureBasic. I have the demo, and have spent all day looking at sample code and tutorials, but I can't figure out how to copy a directory recursively and only overwrite those file which have been modified. I was going to go through a foreach loop and compare the modified dates of the source and destination, but that seems like WAY too much effort!
Can anyone help out?
Thanks in advance!
tcb
Code: Select all
RunProgram("xcopy", "from to /D")

Yes it's a bit of work. It's a reason they included a program to do it.
Here is a procedure to list files recursively:
Code: Select all
Procedure ExamineDirectoryRecursive(Path.s, Pattern.s, List.s())
Protected Dir = ExamineDirectory(#PB_Any, Path, Pattern)
If Right(Path, 1) <> "\"
Path + "\"
EndIf
If Dir
While NextDirectoryEntry(Dir)
If DirectoryEntryName(Dir) <> "." And DirectoryEntryName(Dir) <> ".."
AddElement(List())
List() = Path + DirectoryEntryName(Dir)
EndIf
Wend
FinishDirectory(Dir)
EndIf
Dir = ExamineDirectory(#PB_Any, Path, "")
If Dir
While NextDirectoryEntry(Dir)
If DirectoryEntryType(Dir) = #PB_DirectoryEntry_Directory
If DirectoryEntryName(Dir) <> "." And DirectoryEntryName(Dir) <> ".."
ExamineDirectoryRecursive(Path + DirectoryEntryName(Dir) + "\", Pattern, List())
EndIf
EndIf
Wend
FinishDirectory(Dir)
EndIf
EndProcedure
NewList List.s()
ExamineDirectoryRecursive("c:\s3graphics\", "*.*", List())
ForEach List()
Debug List()
Next
Thank you for the reply!
I guess I should use RunProgram, but I can't figure out how to capture the error levels in the event xcopy fails. I tried the OnError library, but that seems to be geared for PB errors only. Is there a way to capture the error level from xcopy?
Here is what I have so far.
Thanks again!
tcb
I guess I should use RunProgram, but I can't figure out how to capture the error levels in the event xcopy fails. I tried the OnError library, but that seems to be geared for PB errors only. Is there a way to capture the error level from xcopy?
Here is what I have so far.
Thanks again!
tcb
Code: Select all
Procedure init()
Global FileName$, Date$, Time$, FileCreation$
If ExamineDirectory(0, "C:\Batch", "LBM.*")
NextDirectoryEntry(0)
FileName$ = DirectoryEntryName(0)
FileName$ = RemoveString(FileName$, "LBM.", 1)
If ExamineDirectory(0, "C:\Batch", FileName$+"*.*")
While NextDirectoryEntry(0)
DFileName$ = DirectoryEntryName(0)
DeleteFile("C:\Batch\"+DFileName$)
Wend
EndIf
Else
MessageRequester("Backup", "Error: No file exists!", 0)
End
EndIf
Date$ = FormatDate("%mm/%dd/%yyyy", Date())
Time$ = FormatDate("%hh:%ii", Date())
FileCreation$ = FormatDate("%mm%dd%yy_%hh%ii", Date())
EndProcedure
Procedure RunXcopy(Source.s, Destination.s)
RunProgram("xcopy", Source+" /D /E /S /C /Y", Destination, 1)
EndProcedure
init()
CreateDirectory("C:\c_sc")
RunXcopy("L:\sc", "C:\c_sc")
Result$ = "Pass" ; Would like to use error checking here
If CreateFile(0, "C:\Batch\"+FileName$+"."+FileCreation$+"."+Result$)
WriteStringN(0, Date$+" "+Time$)
CloseFile(0)
Else
MessageRequester("Backup", "Error: Can't create file!", 0)
End
EndIf
End
I looked at that and here is what I came up with:
I am using the ExitCode to determine success or failure, but I only get NULL or 0 as teh various outputs. I get output info from the example in the help file, but nothing I try with xcopy works. Not even changing the help example to /? xcopy gets me anything. Shouldn't the following give me a message box with the help text from xcopy?
Thanks again!
tcb
Code: Select all
Procedure RunXcopy(Source.s, Destination.s)
Global Source$, ExitCode
Source$ = Source
Xcopy = RunProgram("xcopy", Source+" /D /E /S /C /Y", Destination, #PB_Program_Open|#PB_Program_Read|#PB_Program_Error)
If Xcopy
While ProgramRunning(Xcopy)
Debug ReadProgramString(Xcopy)
Debug ReadProgramError(Xcopy)
Debug AvailableProgramOutput(Xcopy)
Debug ProgramFilename()
Wend
Debug ProgramExitCode(Xcopy)
Debug IsProgram(Xcopy)
Debug Source
ExitCode.l = ProgramExitCode(Xcopy)
Else
ExitCode.l = 999
EndIf
EndProcedure
Code: Select all
Compiler = RunProgram("c:\windows\system32\xcopy.exe", "/?", "", #PB_Program_Open|#PB_Program_Read)
Output$ = ""
If Compiler
While ProgramRunning(Compiler)
Output$ + ReadProgramString(Compiler) + Chr(13)
Wend
Output$ + Chr(13) + Chr(13)
Output$ + "Exitcode: " + Str(ProgramExitCode(Compiler))
EndIf
MessageRequester("Output", Output$)
tcb
It should, but it looks like this may an issue with xcopy. I checked around on Google, and others have reported problems launching xcopy with commands like RunProgram() using other languages. Using API directly gives the same result. You may want to try creating and calling a batch file from within your program, redirecting xcopy's output to a file and reading that back in:imtcb wrote:...
Shouldn't the following give me a message box with the help text from xcopy?
...
Code: Select all
If CreateFile(0, "C:\xcopy_.bat")
WriteStringN(0, "C:\WINDOWS\system32\xcopy.exe /? >C:\xcopyResults.txt 2>C:\xcopyError.txt")
CloseFile(0)
RunProgram("cmd.exe", "/c C:\xcopy_.bat", "", #PB_Program_Wait)
If ReadFile(0, "C:\xcopyResults.txt")
Debug "xcopyResults:"
Repeat
Debug ReadString(0)
Until Eof(0)
CloseFile(0)
Debug ""
Else
Debug "Unable to open/read xcopyResults.txt"
EndIf
If ReadFile(0, "C:\xcopyError.txt")
Debug "xcopyError:"
Repeat
Debug ReadString(0)
Until Eof(0)
CloseFile(0)
Debug ""
Else
Debug "Unable to open/read xcopyError.txt"
EndIf
EndIf
HTH
-Clutch
"Ahead one third... ahead two thirds... Full ahead flank
And out from the belly of the whale came a prophet, Amen"
And out from the belly of the whale came a prophet, Amen"
Thank you everyone who helped me (especially Trond). I finally went through the work to do it the right way instead of trying to rely on xcopy. I have it working remarkable well.
It creates missing directories
It copies any file not matching date modified
Optionally deletes files in the destination that are not in the source
It has error handling and reporting
It runs on a timer
It minimizes to the tasktray
I just need to get it to read settings from a pref file (working on it) and FTP/Email results and I'm done.
Thanks again!
tcb
It creates missing directories
It copies any file not matching date modified
Optionally deletes files in the destination that are not in the source
It has error handling and reporting
It runs on a timer
It minimizes to the tasktray
I just need to get it to read settings from a pref file (working on it) and FTP/Email results and I'm done.
Thanks again!
tcb
Xcopy.exe has been put on the back burner by Microsoft since XP came out.
Check out the many Shell functions, such as SHFileOperation that can help without reverting to MS-DOS.
E.G. A long time ago (when I was learning PB), I wrote a small PB program to move/copy files using SHFileOperation procedures.....
Check out the many Shell functions, such as SHFileOperation that can help without reverting to MS-DOS.
E.G. A long time ago (when I was learning PB), I wrote a small PB program to move/copy files using SHFileOperation procedures.....
Code: Select all
;======================================================================
; ShareCopy.PB - A Muli-Function Copy Tool that uses: Shell32.dll
; I found a subroutine on VB web-site - author unknown
; modified for PureBasic - Public Domain
; Originally written in 2002
; Bob Houle - updated Jan 23/07 PB 4.02
;======================================================================
#Window1 = 1
#W1Btn1 = 1
#W1Btn2 = 2
#W1Btn3 = 3
#W1Btn4 = 4
#W1Btn5 = 5
#W1String1 = 6
#W1String2 = 7
#W1Check1 = 8
#W1Check2 = 9
#W1Check3 = 10
#W1Check4 = 11
#W1Check5 = 12
#W1Text1 = 13
#W1Text2 = 14
#Window1Flags = #PB_Window_MinimizeGadget | #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_WindowCentered
#Text1Flags = #PB_Text_Right
#Text2Flags = #PB_Text_Right
;======================================================================
; [ Declares ]
;======================================================================
Declare MyWindowCallback(WindowID, Message, wParam, lParam)
Declare Button_Click(Index.l)
;======================================================================
; [ Globals ]
;======================================================================
Global SHFileOp.SHFILEOPSTRUCT ;Windows API Structure
;========================================================================================
WinW=500
WinH=230 ; Window sizes.
hWnd.l = OpenWindow(#Window1,0,0,WinW,WinH,"Window-Like File Operations",#Window1Flags)
If CreateGadgetList(WindowID(1))
ButtonGadget(#W1Btn1,7,200 ,89,25,"Copy")
ButtonGadget(#W1Btn2,105,200 ,89,25,"Move")
ButtonGadget(#W1Btn3,205,200 ,89,25,"Rename")
ButtonGadget(#W1Btn4,305,200 ,89,25,"Delete")
ButtonGadget(#W1Btn5,405,200 ,89,25,"Quit", 1)
StringGadget(#W1String1,220,8,250,21,"")
StringGadget(#W1String2,220,30 ,250,21,"")
CheckBoxGadget(#W1Check1,90,80 ,391,17,"Don't display a progress dialog box")
CheckBoxGadget(#W1Check2,90,100 ,403,17,"Respond with 'Yes to all' for any dialog box that is displayed")
CheckBoxGadget(#W1Check3,90,120 ,404,17,"Rename the file (eg:'Copy #1 of...') if the target name already exists")
CheckBoxGadget(#W1Check4,90,140 ,384,17,"Do not confirm the creation of a new directory if the operation requires it")
CheckBoxGadget(#W1Check5,90,160 ,398,17,"Perform the operation only on files if a wildcard filename (*.*) is specified")
TextGadget(#W1Text1,50,12 ,161,17,"Source File or Folder", #Text1Flags)
TextGadget(#W1Text2,50,35,161,17,"Destination File or Folder", #Text2Flags)
EndIf
If hWnd
;Message Loop
; ----- Windows Messages
; Callback is not required for this App, but included anyways :)
; Might want to reply to Windows messages.
; For now simply try re-sizing the window
SetWindowCallback(@MyWindowCallback())
; ----- PB specific messages
Repeat
EventID.l = WaitWindowEvent()
Select EventID
Case #PB_Event_Gadget
Select EventGadget()
Case #W1Btn1 ;----------Copy
Button_Click(0)
Case #W1Btn2 ;----------Move
Button_Click(1)
Case #W1Btn3 ;----------Rename
Button_Click(2)
Case #W1Btn4 ;----------Delete
Button_Click(3)
Case #W1Btn5 ;----------Quit
EventID = #PB_Event_CloseWindow
EndSelect
EndSelect
Until EventID = #PB_Event_CloseWindow
EndIf
End ; program finish
; *********************************************************************
; [ Required Procedures ]
; *********************************************************************
;======================================================================
; [ Callback Procedure ]
;======================================================================
Procedure MyWindowCallback(WindowID, Message, wParam, lParam)
Result = #PB_ProcessPureBasicEvents
Select message
Case #WM_SIZE ;just testing
Beep_(50,50)
EndSelect
ProcedureReturn Result
EndProcedure
;======================================================================
; [ SHFileOperation API Procedure ]
;======================================================================
Procedure.l Button_Click(Index.l)
;define variables
lFileOp.f
lresult.l
lFlags.w
;Get status of checkboxes
ChkDir.l = GetGadgetState(#W1Check4)
ChkFilesOnly.l = GetGadgetState(#W1Check5)
ChkRename.l = GetGadgetState(#W1Check3)
ChkSilent.l = GetGadgetState(#W1Check1)
ChkYesToAll.l = GetGadgetState(#W1Check2)
;Get the edit box values
FromDirectory.s = GetGadgetText(#W1String1)
ToDirectory.s = GetGadgetText(#W1String2)
;Find out which button was pressed
Select Index
Case 0
lFileOp = #FO_COPY
Case 1
lFileOp = #FO_MOVE
Case 2
lFileOp = #FO_RENAME
Case 3
ChkYesToAll = 0 ;No mattter what - confirm Deletes! Prevents OOPS!
lFileOp = #FO_DELETE
EndSelect
If ChkSilent:lFlags = lFlags | #FOF_SILENT: EndIf
If ChkYesToAll: lFlags = lFlags | #FOF_NOCONFIRMATION:EndIf
If ChkRename: lFlags = lFlags | #FOF_RENAMEONCOLLISION: EndIf
If ChkDir: lFlags = lFlags | #FOF_NOCONFIRMMKDIR: EndIf
If ChkFilesOnly: lFlags = lFlags | #FOF_FILESONLY: EndIf
; NOTE: If you add the #FOF_ALLOWUNDO Flag you can move
; a file to the Recycle Bin instead of deleting it.
SHFileOp\wFunc = lFileOp
SHFileOp\pFrom = @FromDirectory
SHFileOp\pTo = @ToDirectory
SHFileOp\fFlags = lFlags
lresult = SHFileOperation_(SHFileOp)
; If User hit Cancel button While operation is in progress,
; the fAnyOperationsAborted parameter will be true
; - see win32api.inc For Structure details.
If lresult <> 0 | SHFileOp\fAnyOperationsAborted:EndIf: ProcedureReturn 0
MessageRequester("Operation Has Completed", "PureBasic Rules!", 0)
ProcedureReturn = lresult
EndProcedure
; ================================================================
- It was too lonely at the top.
System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
Thanks again blueb!
It still needs a lot of work, but here is where I am now:
It still needs a lot of work, but here is where I am now:
Code: Select all
Procedure ExamineDirectoryRecursive(Path.s, Pattern.s, List.s())
Protected Dir = ExamineDirectory(#PB_Any, Path, Pattern)
If Right(Path, 1) <> "\"
Path + "\"
EndIf
If Dir
While NextDirectoryEntry(Dir)
If DirectoryEntryName(Dir) <> "." And DirectoryEntryName(Dir) <> ".."
If DirectoryEntryType(Dir) = #PB_DirectoryEntry_Directory
AddElement(List())
List() = Path + DirectoryEntryName(Dir) + "\"
Else
AddElement(List())
List() = Path + DirectoryEntryName(Dir)
EndIf
EndIf
Wend
FinishDirectory(Dir)
EndIf
Dir = ExamineDirectory(#PB_Any, Path, "")
If Dir
While NextDirectoryEntry(Dir)
If DirectoryEntryType(Dir) = #PB_DirectoryEntry_Directory
If DirectoryEntryName(Dir) <> "." And DirectoryEntryName(Dir) <> ".."
ExamineDirectoryRecursive(Path + DirectoryEntryName(Dir) + "\", Pattern, List())
EndIf
EndIf
Wend
FinishDirectory(Dir)
EndIf
EndProcedure
Procedure.s tcb_Copy(Source.s, Destination.s, Match) ;Match is a #True / #False to delete files in destination that are not in source
Result$ = "pass"
SourceDir$ = Source
DestinationDir$ = Destination
CreateDirectory(DestinationDir$)
NewList SourceList.s()
ExamineDirectoryRecursive(SourceDir$, "*.*", SourceList())
NewList DestinationList.s()
ExamineDirectoryRecursive(DestinationDir$, "*.*", DestinationList())
If Match
ResetList(DestinationList())
While NextElement(DestinationList())
Result = -1
ForEach SourceList()
If RemoveString(DestinationList(), DestinationDir$) = RemoveString(SourceList(), SourceDir$) : Result = 1 : Break : EndIf
Next
If Result = -1
If Right(DestinationList(), 1) = "\"
Result = DeleteDirectory(DestinationList(), "", #PB_FileSystem_Recursive|#PB_FileSystem_Force)
If Result = 0 : Result$ = "fail" : EndIf
Else
Result = DeleteFile(DestinationList())
If Result = 0 : Result$ = "fail" : EndIf
EndIf
EndIf
Wend
EndIf
ResetList(SourceList())
While NextElement(SourceList())
Result = -1
ForEach DestinationList()
If RemoveString(SourceList(), SourceDir$) = RemoveString(DestinationList(), DestinationDir$)
Result = 1
If Right(SourceList(), 1) = "\" : Break : EndIf
If GetFileDate(SourceList(), #PB_Date_Modified) = GetFileDate(DestinationList(), #PB_Date_Modified) : Break : EndIf
Result = CopyFile(SourceList(), DestinationList())
If Result = 0 : Result$ = "fail" : EndIf
Break
EndIf
Next
If Result = -1
If Right(SourceList(), 1) = "\"
Result = CreateDirectory(DestinationDir$+RemoveString(SourceList(), SourceDir$))
If Result = 0 : Result$ = "fail" : EndIf
Else
Result = CopyFile(SourceList(), DestinationDir$+RemoveString(SourceList(), SourceDir$))
If Result = 0 : Result$ = "fail" : EndIf
EndIf
EndIf
Wend
ProcedureReturn Result$
EndProcedure
Test this freeware: xxcopy
http://www.xxcopy.com
example for cloner two repertories: xxcopy /FF /clone c:\rep d:\rep
attention of reading documentation before launching this order
I tested many programs of this kind, xxcopy is fastest
PS: afflicted for faults, translation in English carried out by software
http://www.xxcopy.com
example for cloner two repertories: xxcopy /FF /clone c:\rep d:\rep
attention of reading documentation before launching this order
I tested many programs of this kind, xxcopy is fastest
PS: afflicted for faults, translation in English carried out by software
-
- Addict
- Posts: 1310
- Joined: Fri Aug 28, 2015 6:10 pm
- Location: Portugal
Re: Copy only modified files
You can write your own see this thread http://www.purebasic.fr/english/viewtop ... 01#p477101
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.