3

I was trying to compare files in 2 folders and copy those new and updated files to a diff folder, for example:

newFolder has  
a\aa.txt (new folder and new file)  
b\aa.txt  
b\ab.exe (modified)  
b\ac.config (new file)  
aa.txt (modified)  
ab.exe (new file)  
ac.config

oldFolder has  
b\aa.txt  
b\ab.exe  
aa.txt  
ac.config

In this case, what I expect in diff folder should be:

diffFolder    
a\aa.txt  
b\ab.exe  
b\ac.config  
aa.txt  
ab.exe

So far I have been searching on google and trying different approaches, but still cannot achieve this.
I have get the files list includes files needed to be copied along with their path by using
xcopy /edyl "newFolder\*" "oldFolder"
and was trying using

for /f %%F in ('xcopy /e /dyl "new\*" "old"') do @xcopy %%F diff /e

this messes up diffFolder
also tried

for /f %%F in ('xcopy /e /dyl "new\*" "old"') do @robocopy new diff %%F /e

this only create directories in diffFolder but not copy files, gave me error: invalid parameter #3 :"newFolder\a\aa.txt"

for /f %%F in ('xcopy /e /dyl "new\*" "old"') do @copy "%%F" "diff" >nul

only copy files not create directories.
I was also trying to use powershell but result the same with @copy.

Any one can help me on this specific issue?

Thanks in advance!

Duke Wang
  • 83
  • 3
  • 10

1 Answers1

4

Since this is tagged with a powershell tag, this is how I would do it in powershell.

First setup some variables with directory names:

#create path variables
$olddir = "C:\oldFolder"
$newdir = "C:\newFolder"
$diffdir = "C:\diffFolder"

Now, get the list of files in each directory using get-childitem with -recurse parameter, piped through where-object to filter out directories:

#Get the list of files in oldFolder
$oldfiles = Get-ChildItem -Recurse -path $olddir | Where-Object {-not ($_.PSIsContainer)}

#get the list of files in new folder
$newfiles = Get-ChildItem -Recurse -path $newdir | Where-Object {-not ($_.PSIsContainer)}

Now, compare the lists, but compare only the LastWriteTime property (can use Length instead of or alongside LastWriteTime - LastWriteTime,Length).

Make sure to use the -Passthru option so that each file is passed through as an object with all file properties still accessible.

Pipe through sort-object to sort on LastWriteTime attribute, so files are processed oldest to newest. Then pipe into a foreach loop:

Compare-Object $oldfiles $newfiles -Property LastWriteTime -Passthru | sort LastWriteTime | foreach {

In the loop, for each file, build the new name retaining the directory structure (replace olddir and newdir paths with diffdir path).

Get just the directory of the new path using Split-Path and test if it exists - if it doesn't, create it using mkdir as copy-item won't create target directory unless copying a directory, not a file.

Then, copy the file (you can use the -whatif option in the copy command to have it just tell you what it would copy, without actually doing it):

     $fl = (($_.Fullname).ToString().Replace("$olddir","$diffdir")).Replace("$newdir","$diffdir")
     $dir = Split-Path $fl
     If (!(Test-Path $dir)){
        mkdir $dir
     }
     copy-item $_.Fullname $fl
}

So the full script is:

#create path variables
$olddir = "C:\oldFolder"
$newdir = "C:\newFolder"
$diffdir = "C:\diffFolder"

#Get the list of files in oldFolder
$oldfiles = Get-ChildItem -Recurse -path $olddir | Where-Object {-not ($_.PSIsContainer)}

#get the list of files in new folder
$newfiles = Get-ChildItem -Recurse -path $newdir | Where-Object {-not ($_.PSIsContainer)}

Compare-Object $oldfiles $newfiles -Property LastWriteTime -Passthru | sort LastWriteTime | foreach {
     $fl = (($_.Fullname).ToString().Replace("$olddir","$diffdir")).Replace("$newdir","$diffdir")
     $dir = Split-Path $fl
     If (!(Test-Path $dir)){
        mkdir $dir
     }
     copy-item $_.Fullname $fl
}
Graham Gold
  • 2,435
  • 2
  • 25
  • 34
  • It took me a bit of experimentation to understand how this works. For each file which exists in both `$olddir` and `$newdir`, they are **both** copied to into `$diffdir`. So the sort order is utmost important: the later file to be copied will be the more recent. ----- What I'm trying to come up with is a version of this which **ignores** files which only exist in `$olddir` (I do want to move new files in `$newdir`). my noodle is kinda getting twisted. This is almost perfect, except that if the file only exists in EITHER directory, it is copied over. – Nate Apr 26 '19 at 00:43
  • CAUTION!! This ignores source files with certain characters, for example the `[` open bracket character in the filename. (Who knows what other characters will cause it to miss the file.) In my testing, this occurs when the `[` character appears in the `Copy-Item` line, specifically, in the **source** variable `$_.Fullname`. This character appearing in the destination variable is fine -- that doesn't matter, as long as the source filename doesn't contain it. Testing was done with quoted literal filenames in the PowerShell console. FWIW, I'm in PowerShell 5.1 on Win10. Can this be escaped? – Nate Apr 26 '19 at 04:29
  • This was quickly thrown together with minimal testing to help the OP with the issue they were facing for their specific use case - no guarantees were given. If you find issues by all means edit to add a fix. Similarly if you are wanting to adapt it to do something different, as one of your comments would suggest, again, feel free. – Graham Gold Apr 26 '19 at 06:09