Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

New Perl programmer looking for answer in Directory Comparisons

by ATaylor (Initiate)
on May 01, 2001 at 19:41 UTC ( [id://77002]=perlquestion: print w/replies, xml ) Need Help??

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

Hello, My corporation let go our last PERL programmer on Friday and yesterday gave me the task of finishing what he was working on. Now I am not a very accomplished PERL programmer. Pretty much I picked up a book last night and read like crazy and have been searching for an answer. The code I inherited is supposed to take two directories as ARGs and then compare them. The results of this comparison are as such: If the file exist in the Source and Destination directories then do nothing. If the file exist in the source directory and not in the destination directory then copy it to the destination. If the file exist in the destination and not in the source directory then erase it from the the destination directory. This seems pretty dern simple. The program is supposed to basically keep an internal web site updated for the employees. The problem is the following script I inherited seems to work except it only does the compare on the very last file in the hash. It load the hash up fine, but does not foreach correctly. My understanding of PERL is very weak and I know this is a poorly written piece of code. Reason enough that the person was let go. Now I am afraid same will happen to me if I don't pick PERL up fast. I am an assembly programmer that was working on embedded drivers before they moved me here. This world is a tad bit different. I included some of my own comments in the code to help with what I think is going on. If anyone has a clue and can give me a hint or help put me in the right direction it would be greatly appreciated. Perhaps I can one day come to embrace PERL (which seems like a rather powerful language.)
#!/usr/bin/perl -w # # Reads contents of a directory # in array context. # # Usage: # readdir4.pl source_directory, destination_directory # use File::Copy; # remove the comment below if you want to read in the directory from a +nd arg $name1 = $ARGV[0]; $name2 = $ARGV[1]; opendir(SOURCE, $name1) or die "Can't open $name1 due to $!"; @source_entries = readdir(SOURCE); closedir(SOURCE); # Lets get the destination now opendir(DESTINATION, $name2) or die "Can't open $name2 due to $!"; @dest_entries = readdir(DESTINATION); closedir (DESTINATION); # Sort Results. @sourcesorted = sort(@source_entries); @destsorted = sort (@dest_entries); # Make a hash out of them foreach $filename1 (@sourcesorted) { %hashsource = ($filename1,$filename1); } foreach $filename2 (@destsorted) { %hashdest = ($filename2,$filename2); } # At this point we have two sorted arrays that we can use to do compar +es with # The idea now is to compare Source with Destination first. # If the file exist in Source and not in Destination then copy it over + to Destination # If the file exist in both then do nothing. # Then compare Destination with Source. If the file exist in Destinati +on and not in source # then erase it from Destination. # this routine will seperate all the files into two arrays @union = @intersection = @difference = (); %count = (); foreach $element (@sourcesorted, @destsorted) { $count{$element}++ + } foreach $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference } +, $element; } # Sorting the difference array @diffsorted = sort(@difference); # Now lets compare the files. We can ignore all the files in the inter +section list # and compare only the files in the difference list. # The trick is to use the @difference array correctly foreach $filename (@diffsorted) { # now that we are going through each element in the @differenc +e array lets # do some checking. # Now for every file in the sorted source list see if it is no +t equal to a file # file in the differences list. If it is in the differences li +st and it is not in the # source list then it should be deleted from the destination d +irectory. unless ( exists $hashdest{$filename}){ if ( exists $hashsource{$filename}){ print "$filename exist in the source directory.\n"; print "Not in the destination directory, so copy it.\n\n"; #now to do actual work in here. chdir($name1); print "Now copying, $filename.\n"; #copy($filename,$name2.$filename) or die # "Can't copy $filename to $name2$filename due to $! +.\n"; } } } foreach $filename (@diffsorted) { # continuation of above test. This one checks for stragglers i +n the # destination directory if ( exists $hashdest{$filename}){ unless ( exists $hashsource{$filename}) { print "$filename exist in destination directory.\n"; print "Not in the source directory, so delete it. \n +\n"; # Lets change to the right directory # chdir($name2); # and delete unlink($name.$filename); print "Deleted $filename.\n"; } } } foreach $entry (@sourcesorted) { print "Source: \t$entry\n"; } foreach $entrydest (@destsorted) { print "Destination: \t$entrydest\n"; } foreach $entryinter (@intersection) { print "Intersection: \t$entryinter\n"; } foreach $entrydiff (@diffsorted) { print "Differences: \t$entrydiff\n"; } # readdir4.pl
Thank you for any help, Aaron Taylor

Replies are listed 'Best First'.
Re: New Perl programmer looking for answer in Directory Comparisons
by merlyn (Sage) on May 01, 2001 at 19:49 UTC
    You can replace all that with this:
    use File::Copy; my ($src,$dst) = @ARGV; -d or die "$_ is not a directory" for $src,$dst; ## first pass, copy anything not there... opendir SRC, $src or die "cannot opendir $src: $!"; for (readdir SRC) { next unless -f "$src/$_"; next if -e "$dst/$_"; print "copying $src/$_ to $dst/$_...\n"; copy "$src/$_", "$dst/$_" or warn "Cannot copy!"; } closedir SRC; ## now delete the ones that don't belong there opendir DST, $dst or die "Cannot opendir $dst: $!"; for (readdir DST) { next unless -f "$dst/$_"; next if -e "$src/$_"; print "deleting $dst/$_...\n"; unlink "$dst/$_" or warn "Cannot unlink: $!"; } closedir DST;

    -- Randal L. Schwartz, Perl hacker

Re: New Perl programmer looking for answer in Directory Comparisons
by chromatic (Archbishop) on May 01, 2001 at 19:51 UTC
    The problem is in these lines. On each iteration, the code clobbers whatever's in the hash already:
    foreach $filename1 (@sourcesorted) { %hashsource = ($filename1,$filename1); }
    To fix it, I'd probably do something like this:
    my %hashsource; @hashsource{@sourcesorted} = @sourcesorted;
    That does what you intend, by assigning a list of keys to a list of values.

    There are other things to correct in the code, but that will fix the issue you described.

      Thank you for the help. I am already on it with the previous suggestions. Thank you again, Aaron Taylor
Re: New Perl programmer looking for answer in Directory Comparisons
by lindex (Friar) on May 01, 2001 at 21:05 UTC

    This is my "There's more than one way to do it", its prolly insane and stupid. But I did it anyways.

    p.s. some borrowed methodology from merlyn.

    #!/usr/bin/perl -w use strict; use File::Copy qw(copy); die("Usage: $0 <src> <dst>\n") unless($#ARGV==1); -d or die("$_ is not a directory") for(@ARGV); s/\/$// for(@ARGV); for(glob("$ARGV[0]/*"),glob("$ARGV[1]/*")) { copy("$ARGV[0]/$_","$ARGV[1]/$_") or warn("$!: $ARGV[0]/$_\n") if(s/$ARGV[0]// && -f "$ARGV[0]/$_" && !-e "$ARGV[1]/$_"); unlink("$ARGV[1]/$_") or warn("$! unlinking $ARGV[1]/$_\n") if(s/$ARGV[1]// && -f "$ARGV[1]/$_" && !-e "$ARGV[0]/$_" ); }



    lindex
    /****************************/ jason@gost.net, wh@ckz.org http://jason.gost.net /*****************************/
      Well, stealing from that:
      use File::Copy; use File::Basename; for $base (map basename $_, map glob "$_/*", @ARGV) { my ($src, $dst) = map "$_/$base", @ARGV; -f $src and not -e $dst and not copy $src, $dst and warn "copy $src +to $dst failed"; -f $dst and not -e $src and not unlink $dst and warn "unlink $dst fa +iled: $!"; }

      -- Randal L. Schwartz, Perl hacker

        Hmm, I wonder if I can make it any shorter ;)
        foiled agian, Yarr


        lindex
        /****************************/ jason@gost.net, wh@ckz.org http://jason.gost.net /*****************************/
Re: New Perl programmer looking for answer in Directory Comparisons
by ZZamboni (Curate) on May 01, 2001 at 22:39 UTC
    Apart from all the other suggestions: for that particular application (keeping two directories in sync) you might want to look at rsync, which is a program designed specifically for that purpose, and which can do remote backups and all sort of other nifty things. Remember: TIMTOWTDI (and not all of them use Perl :-) )

    --ZZamboni

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://77002]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2024-04-23 19:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found