habhab has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks ! I am a beginner looking for some help.
I have 2 directories, Source and Target and I would like to take every directory/file in Source dir that is not in Target dir and copy it over to Target dir. This should include sub dirs as well.
If dir/file from Source is already in Target, I would like to print its dir/file name.
So far I came up with the follwoing code, but it doesnt seem to be working.
Usage: perl mergeTrees.pl SourceDir TargetDir
use strict; use File::Find; use File::Copy; use Cwd; my $Src = $ARGV[0]; my $Trg = $ARGV[1]; my $path = getcwd(); find (\&wanted, $Src); sub wanted { opendir (DIR, "$path/$Trg") or die "cannot opendir $Trg"; foreach my $file (readdir(DIR)) { if ($_ == $file) { print "File already in $Trg: $file\n"; } else { copy("$_","$path/$Trg/$_"); } } closedir (DIR); }
  • Comment on How can I compare two directories and copy over files missing from one to another?
  • Download Code

Replies are listed 'Best First'.
•Re: How can I compare two directories and copy over files missing from one to another?
by merlyn (Sage) on Sep 18, 2004 at 00:12 UTC
      And then use real rsync.
Re: How can I compare two directories and copy over files missing from one to another?
by JediWizard (Deacon) on Sep 17, 2004 at 19:16 UTC

    My first piece of advise is that there is a much easier way to test if a file exists in a directory than reading its contents and iterating through each one doing a comparison. To see if a file exists try:

    if(-f $path_to_file){ #do something }

    The second problem I see is that $_ will be set to the name of a file, so if the file is in a subdirectory (and you said you wanted to include subdirs) testing -f $path.'/'.$Trg.'/'.$_ would not work. Try this:

    use strict; use File::Find; use File::Copy; use Cwd; my $Src = $ARGV[0]; my $Trg = $ARGV[1]; my $path = getcwd(); chdir($Src); find (\&wanted, '.'); sub wanted { if(-f $path.'/'.$Trg.'/'.$File::Find::dir.'/'.$_){ print "File in $Trg: $_\n"; }else{ mkdir($path.'/'.$Trg.'/'.$File::Find::dir) if(! -d $path.'/'.$Tr +g.'/'.$File::Find::dir); copy($_, $path.'/'.$Trg.'/'.$File::Find::dir.'/'.$_) if(! -d +$File::Find::name); } }
    May the Force be with you
Re: How can I compare two directories and copy over files missing from one to another?
by TheEnigma (Pilgrim) on Sep 17, 2004 at 19:27 UTC
    The logic in your sub wanted is flawed. Consider this scenario.

    Source directory contains files: 'foo', 'baz, 'blurch'
    Target directory contains files: 'foo', 'bar'

    find (\&wanted, $Src); will take the first file, 'foo' and call wanted with $_ == 'foo'. wanted will compare 'foo' to 'foo' and print "File already in $Trg: $file\n". So far, so good. Then it will compare 'foo' to 'bar', and copy 'foo' to the target directory, where it already exists. And according to the docs for File::Find:

    Trying to copy a file on top of itself is a fatal error.

    Even if it survived that, if you follow the logic, it will be saying that the file is already there, when it wasn't, but it just happened to copy it in an earlier iteration of the for loop.

    I haven't tested this, but I think you need to do this:

    sub wanted { opendir (DIR, "$path/$Trg") or die "cannot opendir $Trg"; foreach my $file (readdir(DIR)) { if ($_ == $file) { print "File already in $Trg: $file\n"; return; } } copy("$_","$path/$Trg/$_"); closedir (DIR); }

    which will first check all the files in target to see if the source file in question exists, and exit the sub if it does. Only if it survives that test will it be copied (and only copied once ;)

    TheEnigma

      Thanks for your replies, guys. Whats good is that I actually understand what's going on.
Re: How can I compare two directories and copy over files missing from one to another?
by paulbort (Hermit) on Sep 17, 2004 at 21:31 UTC
    First, rsync already does this, so you might want to save yourself a whole bunch of time and just use it.

    If you're using this as a learning experience, or you're stuck in an environment without rsync, there's one other thing to watch out for: What do you do when the source is a file and the target has a directory with the exact same name? Or if the source has a directory and the target has a file? The answers depend on what you really need the functionality for.

    --
    Spring: Forces, Coiled Again!
Re: How can I compare two directories and copy over files missing from one to another?
by steves (Curate) on Sep 18, 2004 at 15:40 UTC