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

Dear Perl Monks,

I'm new to Perl, and I'm trying to zip all of the text files in and below one directory. I then need to put that zip file into another directory.

It prints all of the elements in both arrays, but the second array contains the files that I need to zip (the second array - @newTextFiles). I run the script and get no errors, but the zipped file (textFiles.zip) that gets created in my destination directory contains nothing.

I'm using the second array to hold only the filenames.txt (with all of the directory names stripped off -- as the first array contains the full paths). I know that I'm doing something wrong, as well as not coding efficiently, but I'm new to Perl. I'm doing this all in Windows using ActiveState Perl 5.10.

# copy.pl # Copy all .txt files from source directory, zip them and send the zip +ped file to destination directory use strict; use warnings; use diagnostics; use File::Find; my @textFiles; # array in which full pathnames are stored my $sourceDir = 'C:/sourceDirectory/One'; # Source directory my $destDir = 'C:/destinationDirectory/Two/'; # Destination directory # Finds files that match criteria (all .txt files) in sourceDir and ad +ds them to array File::Find::find( \&add_to_array, $sourceDir ); sub add_to_array # subroutine that adds .txt files to the array { push @textFiles, $File::Find::name if ( /\.txt$/); # adds files t +o textFile array using File::Find::name if they end in .txt } # This while loop will be used to get rid of the directory names and a +dds just the filenames to the new array (newTextFiles) my $counter = 0; # initialize counter to zero my $num = @textFiles; # gets total number of elements in the textFi +les array my @newTextFiles; # initialize new array in which just filenames will + be stored my $position; # initialize variable that seperates the directory fr +om the filename my $shortFileName; # initialize variable that gets the filenames # Loops through while loop the same number of times as there are eleme +nts in textFile array while ($counter < $num) { $position = rindex($textFiles[$counter],"/") + 1; # finds las +t occurance of '/' that seperates directories from filenames $shortFileName = substr($textFiles[$counter], $position); # e +xtracts the filenames push @newTextFiles, $shortFileName; # adds the filenames (wit +hout the directory names) to the new array (newTextFiles) print "$newTextFiles[$counter]\n"; # prints just the filename +s $counter++; # increments count by one until all files from ol +d array are added to new array } # Zips the files in array use Archive::Zip qw( :ERROR_CODES :CONSTANTS); my $zip = Archive::Zip->new(); # new instance foreach my $zipFiles (@newTextFiles) { $zip->addFile($zipFiles); # add files } if ($zip->writeToFileNamed("$destDir/textFiles.zip") != AZ_OK) { print "Error creating archive!\n"; } else { print "Archive successfully created!\n"; print "@newTextFiles\n"; }
This will eventually be used to copy thousands of .txt files from a Windows box into a MySQL db on a Linux box. Any help is appreciated.

Replies are listed 'Best First'.
Re: Zipping an array problem
by hipowls (Curate) on Feb 17, 2008 at 02:50 UTC

    Not quite sure what you want to achieve but I'm guessing that you want to add text files to a zip archive without preserving the original directory structure.

    Zip::Archive needs to know the path as well as the file name, how else can it find it? You don't need to keep a separate array of the short names, you calculate it when required.

    use Archive::Zip; use File::Find; use File::Basename; my $sourceDir = 'C:/sourceDirectory/One'; my $destDir = 'C:/destinationDirectory/Two/'; my @files; find( \&files_to_archive, $sourceDir ); my $zip = Archive::Zip->new(); foreach my $file (@files) { $zip->addFile( $file, basename $file ) } if ($zip->writeToFileNamed("$destDir/textFiles.zip") != AZ_OK) { print "Error creating archive!\n"; } else { print "Archive successfully created!\n"; } sub files_to_archive { push @files, $File::Find::name if (/\.txt$/); }

      hipowls,

      You rock. That did it. Thanks so much for your help. You've saved me a lot of hours of trying to get that to work. Thanks again.

      hipowls,

      Yes, I did want to add text files to a zip archive without preserving the original directory structure. Thanks again. You fixed it with less that half the lines of code, and in about 1/100th the time it took me to write that horrible looking stuff I was calling code.

      One more question: with using the foreach loop, how do I create a loop so that I only zip 3000 files at a time, and never zip the same files more than once? I know that I need to use a loop, and add a number to the end of the zipped file (ex. - textFiles1.zip, textFiles2.zip, etc.) to differentiate them so the zipped files are not overwritten in the destination directory (it will ultimately be a MySQL db) each time.

      I've been working today for 14 hours, and I'm getting more confused the more I think about it.

      I'll next be working on unzipping the textFiles.zip and dropping them in the db.

        Get some rest;) You'll work better for it.

        You need to chop @files into chunks of 3,000, or less for the end. I'll not duplicate all the code, this is just an outline.

        use List::Util qw(min); foreach my $seq ( 0 .. $#files/3_000 ) { my $zip = Archive::Zip->new(); my $start = $seq * 3_000; my $end = min ( $#files, $start + 2_999 ); foreach my $i ( $start .. $end ) { $zip->addFile( $files[$i], basename $files[$i] ); } $zip->writeToFileNamed("test-$seq.zip"); }