1

I'm trying to combine two directories on a Windows 7 machine that have some of the same subfolders (but not all) and some of the same files (but not all). I'd like to copy

For example, I have the following directory structure and want to combine (copy or move) "Dir_A" and "Dir_Z". Where the file exists in the destination directory, I want to compare filesizes and keep the larger of the two files.

Dir A
    Dir B
        file 1.txt
        file 2.txt
        file 3.txt
    Dir C
        file 4.txt
    Dir D
        file 5.txt
        file 6.txt
Dir_Z
    Dir Y
        file 8.txt
    Dir C
        file 4.txt
    Dir D
        file 6.txt

I'm comfortable using a cmd file or powershell. Thank you in advance.

EDIT: Modify to include spaces in the director and file names and add what I have so far.

Here's what I've been able to do so far. It appears to work with the exception of when there are spaces in the directory or filenames.

@echo off
echo :: Combine two folders, Keep the larger of any given files, Delete the other

if "%1"=="" goto :NoParam
if "%2"=="" goto :NoParam
setlocal enabledelayedexpansion
set SOURCE_DIR=%1
set DEST_DIR=%2

for /R "%SOURCE_DIR%" %%S in (*) do (
echo Source: "%%S"
@echo off

for /f "delims=" %%R in ('call MakeRelative.cmd "%%S" "%SOURCE_DIR%"') do ( 
set FILE_REL="%%R"

set filename=%DEST_DIR%\%%R
For %%D in ("!filename!") do (
Set Name=%%~nxD

if exist %DEST_DIR%\%%R (
    echo File Exists
if %%~zD lss %%~zS (
    echo Destination %%~zD is smaller than Source %%~zS
    call robocopy %%~dpS %%~dpD "!Name!" /MOV
) else  del %%S && Echo File is not larger, Deleting %%S

) else call robocopy %%~dpS %%~dpD "!Name!" /MOV
)
)
)

Pause
goto :eof

:NoParam
echo.
echo Syntax: %0 [Source_DIR] [Dest_DIR]
goto :eof

I make use of the MakeRelative.cmd as outlined here: http://www.dostips.com/DtCodeCmdLib.php#MakeRelative

I apologize that this is not elegant.

SRTheo
  • 13
  • 3

1 Answers1

0

I'm not much of a PowerShell developer, but this should do the work and is hopefully somewhat more obvious regarding what it's doing than the batch file.

if($args.length -ne 3) {
    "usage: PowerShell ThisScriptName.ps1 -command ""SourceDirectory"" ""DestinationDirectory""";
    Exit
}

function CopyFileIfSourceIsLarger($SourceFile, $DestinationFile) {
    $S = New-Object IO.FileInfo($SourceFile.ToString());
    $D = New-Object IO.FileInfo($DestinationFile.ToString());
    if ($S.Length -gt $D.Length ) {
        "Overwriting smaller file " + $D.FullName;
        [IO.File]::Copy($S.FullName, $D.FullName, $true);
    }
}

$SourceDir = New-Object IO.DirectoryInfo($args[1]);
$DestinationDir = New-Object IO.DirectoryInfo($args[2]);
$SourceDirectoryPath = $SourceDir.FullName;
$DestinationDirectoryPath = $DestinationDir.FullName;

"Source Path: " + $SourceDirectoryPath;
"Destination Path: " + $DestinationDirectoryPath;

$SourceItems = Get-ChildItem -recurse $SourceDir;

"Synchronizing Directory Structure..."
foreach ($dir in $SourceItems | ? { $_ -is [IO.DirectoryInfo] })  {
  $sourceRelative = $dir.fullname.Substring($SourceDirectoryPath.length + 1);
  if ([IO.Directory]::Exists([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) -eq $false) {
    "Creating folder " + [IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative);
    [IO.Directory]::CreateDirectory([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) | out-null;
  }
}

"Synchronizing Files..."
foreach ($file in $SourceItems | ? { $_ -is [IO.FileInfo] })  {
    $sourceRelative = $file.fullname.Substring($SourceDirectoryPath.length + 1);
    if ([IO.File]::Exists([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) -eq $true) {
        CopyFileIfSourceIsLarger ([IO.Path]::Combine($SourceDirectoryPath,$sourceRelative).ToString()) ([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative).ToString());
    } else {
        "Creating file " + [IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative);
        [IO.File]::Copy([IO.Path]::Combine($SourceDirectoryPath,$sourceRelative).ToString(),[IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative).ToString()) | out-null;
    }
}

"All done."

#Thanks to:
#"Identifying Object Types": http://powershell.com/cs/blogs/tobias/archive/2008/10/14/identifying-object-types.aspx
#"Exiting early from foreach": http://stackoverflow.com/questions/10277994/powershell-how-to-exit-from-foreach-object
#"Calling functions with multiple parameters" http://stackoverflow.com/questions/4988226/powershell-multiple-function-parameters

When the script runs, files 1,2,3,5,9, and 10 get copied, and file 6 gets overwritten because it is smaller in the destination.

Here is a batch file that sets up your test case (plus some extra folders to test spaces in the names). I have commented out the two lines that clear out directories A and Z for safety but you can remove the REM statements if you are unconcerned about deleting files in a folder A and Z in the current directory.

@ECHO OFF

REM Uncomment at own risk RD /S /Q A
REM Uncomment at own risk RD /S /Q Z

MD "A\B"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 1.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 2.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 3.txt"
MD "A\C"
echo aaaaaaaabbbbbbbbcccccccc>"A\C\file 4.txt"
MD "A\D"
echo aaaaaaaabbbbbbbbcccccccc>"A\D\file 5.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\D\file 6.txt"
MD "A\FOLDER E"
echo aaaaaaaabbbbbbbbcccccccc>"A\FOLDER E\file 9.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\FOLDER E\file 10.txt"
MD "Z\Y"
echo aaaaaaaabbbbbbbbcccccccc>"Z\Y\file 8.txt"
MD "Z\C"
echo aaaaaaaabbbbbbbbccccccccdddddddd>"Z\C\file 4.txt"
MD "Z\D"
echo aaaaaaaa>"Z\D\file 6.txt"
Joey
  • 344,408
  • 85
  • 689
  • 683
NYCdotNet
  • 4,500
  • 1
  • 25
  • 27
  • **Thank You** @NYCdotNet and @Joey! This appears to work. I verified this with your test script and with myown test setup. I've got it running now on my production data. It hiccuped when I used relative paths as arguments, but not with absolute paths. Thank you heaps. – SRTheo Sep 23 '13 at 05:39
  • By "Hiccup" I mean that instead of appending the relative address to the address of the current folder, it appends the relative address to the "Home" folder located at C:\Users\#####\. Consequently, it errored because the folders didn't exist. – SRTheo Oct 22 '13 at 15:54