How can I zip up certain files and directories recursively?

Using ActivePerl for windows xp professional. I have Archive::Zip installed, but ideally I would like to make this available for anyone to use without them having to install it- so first off, can I include the library within this file? If not, that's ok I guess, but I'd prefer the user to not have to do install it separately.

Ok, here's what I'm trying to do:

I have several directories under a main directory named extensions, each one with a long string, i.e. {5faf073c-caa3-40f5-a67b-2eda36514a7b}
In each of these directories, there is a file called install.rdf, possibly another file called install.js, as well as another directory called "chrome" that has additional files under it.

I want to go through each of these long-named directories, search through the install.rdf file, grab the name that is between the tags:
<em:name> and </em:name>, and store that name as a variable. Then I want to zip up the install files and directories within each one, and call it (variable name that was taken from the <em:name> tags).xpi.

Sorry the explanation is long-winded, but it's kind of complicated. Here's what I have so far, which I think is pretty close, but I get an error of:

Can't access (variable that is stored): No such file or directory at C:\firefox stuff\repack.pl line 22, <IN> line 32.

[code]
#!/usr/bin/perl
use strict;
use POSIX;
use File::Copy;
use File::Basename;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);

my $source="C:\\download\\test\\extensions";
my $target="C:\\download\\files";
my $n="em:name";
my $zip = Archive::Zip->new();

opendir(DIR,$source);
foreach(readdir(DIR)) {
   open(IN,"$source/$_/install.rdf");
   foreach(<IN>) {
      if (/<$n>(.*)<\/$n>/){
            $zip->addTree( '$_', '$1' ) or die("Can't access $1: $!");
               }
   }  
   close(IN);
}
closedir(DIR);

$zip->writeToFileNamed('$1.xpi');
[/code]
roachfiendAsked:
Who is Participating?
 
kanduraConnect With a Mentor Commented:
I see. Item 1) was my mistake. Item 2) is a matter of moving the "$zip = new Archive::Zip" into the inner loop.

#!/usr/bin/perl
use strict;
use POSIX;
use File::Copy;
use File::Basename;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);

my $source="C:\\download\\test\\extensions";
my $target="C:\\download\\files";
my $n="em:name";

opendir(DIR,$source) or die "Error opening $source: $!";
foreach my $dir(readdir(DIR)) {
    next unless -d "$source/$dir";              ### skip non-directory entries
    next if $dir =~ /^\.+$/;                    ### skip . and ..

    if(open(IN, "$source/$dir/install.rdf")) {  ### always check the result from open
        foreach(<IN>) {
            if (/<$n>(.*)<\/$n>/) {
                my $name = "$1.xpi";
                my $zip = new Archive::Zip;
                $zip->addTree( "$source/$dir", '' );
                $zip->writeToFileNamed($name);
            }
        }
    }  
}
0
 
roachfiendAuthor Commented:
update- this is closer, but it's not quite there yet. it's adding in whole directories instead of the specific ones:

#!/usr/bin/perl
use strict;
use POSIX;
use File::Copy;
use File::Basename;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);

my $source="C:\\download\\test\\extensions";
my $target="C:\\download\\files";
my $n="em:name";
my $zip = Archive::Zip->new();

opendir(DIR,$source);
foreach(readdir(DIR)) {
   open(IN,"$source/$_/install.rdf");
   foreach(<IN>) {
      if (/<$n>(.*)<\/$n>/){
                  my $name="$1.xpi";
                  $zip->addTree( $source, $1 );
                     $zip->writeToFileNamed($name);
         }
   }  
  close(IN);
}
closedir(DIR);
0
 
TalmashCommented:
try this simple one

# built array of files and directories to zip .
@list_of_files_and_direct = ("1st_file" , "2nd_file" ,... , "1st_dir" , "2nd_dir" ...);

# tar them all together
system("tar cvf tmp.tar @list_of_files_and_directories");

# now just zip the tar file
system("gzip tmp.tar");

it may not very "clean" but sometimes there is no need to look for the best solution .
solution that works and quickly found , sometimes better .

sometimes when schedule push me to finish quicker , I solve things like that .

good luck

tal
0
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
kanduraCommented:
don't you mean to add the directory "$source/$_" ?

    $zip->addTree( $source.'/'.$_ , $1 );

0
 
roachfiendAuthor Commented:
Talmash, unfortunately that isn't an option. There are about 50 different directories, each one of them named something as obtuse as the example I listed in my first post. Within each of these are other files that may or may not exist. Also, I don't want a huge zipped file, I want individual ones of each directory.

Kandura, that does seem to be what I want, but that gives me an error:

Unsuccessful stat on filename containing newline at C:/Perl/lib/File/Find.pm line 632, <IN> line 32.
0
 
kanduraCommented:
The newline comes from <IN>. The problem is that the outer $_ (from the readdir) is inaccessible in the inner loop (from the foreach).
Try this instead:

foreach my $dir(readdir(DIR)) {
    next unless -d "$source/$dir";              ### skip non-directory entries
    next if $dir =~ /^\.+$/;                    ### skip . and ..

    if(open(IN, "$source/$dir/install.rdf")) {  ### always check the result from open
        foreach(<IN>) {
            if (/<$n>(.*)<\/$n>/) {
                $zip->addTree( "$source/$dir", "$1.xpi" );
                $zip->writeToFileNamed($name);
            }
        }
    }  
}
0
 
roachfiendAuthor Commented:
Getting closer! Now it has the zip structure of this:

extension_name.zip
|
|- extension_name (which is a folder)
      |-chrome
      |-other files

So I just want the chrome and other files underneath the extension_name.zip, I don't want the extension_name folder there.

Also, it will add in this entire series of files and folders to the next, which builds up each zip file with the previous one, so each file is progressively larger. I'd like it to "restart", if you will, for each new entry.
0
 
roachfiendAuthor Commented:
Yay!!! Thank you so much, it works perfectly.
0
 
ozoCommented:
shouldn't
if (/<$n>(.*)<\/$n>/){
be
for( /<$n>(.*?)<\/$n>/g ){
0
 
roachfiendAuthor Commented:
One more quick question, if it would be easy to implement-

Could I have it skip a folder if it's named "uninstall" within each directory?
0
 
kanduraCommented:
I think so:

    $zip->addTree( "$source/$dir", '', sub { !/^uninstall$/ } );

should skip any file or directory named "uninstall".
0
 
roachfiendAuthor Commented:
Hmm, doesn't work.
0
 
kanduraCommented:
maybe A::Z passes in a complete path. The docs are not 100% clear on this: "[the third argument to addTree] is an optional subroutine reference to select files: it is passed the name of the prospective file or directory using $_, and if it returns true, the file or directory will be included. The default is to add all readable files and directories."

You might have success with /uninstall$/ in that sub.
0
 
roachfiendAuthor Commented:
sub { !/uninstall/ };

worked. Thanks again kandura!
0
 
kanduraCommented:
You're welcome :-)

Just out of curiosity, what are you doing with those firefox extensions?
0
 
roachfiendAuthor Commented:
I'm just making a backup script. After you install an extension, the .xpi file doesn't get saved, so I want to be able to save them at some point, and this is a (now) easy way to do it. I have a few other scripts that back up your user information, like bookmarks, and things like that.

Obviously perl is kind of foreign to me, so thanks for the help. I'd like to be able to write an extension that does this, but xul isn't nearly as flexible as perl. And for anyone who is curious about authoring extensions for Firefox, I wrote a tutorial for it, maybe you'd like to check it out. It's easier than the above scripts!

http://extensions.roachfiencd.com/howto.php
0
 
roachfiendAuthor Commented:
argh, misspelled my own name. that link above should be

http://extensions.roachfiend.com/howto.php
0
 
kanduraCommented:
neat :)
I actually use your BugMeNot extension, how cool ;)
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.