Solved

How can I zip up certain files and directories recursively?

Posted on 2004-09-25
18
526 Views
Last Modified: 2012-08-13
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]
0
Comment
Question by:roachfiend
18 Comments
 

Author Comment

by:roachfiend
ID: 12153501
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
 
LVL 6

Expert Comment

by:Talmash
ID: 12153839
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
 
LVL 18

Expert Comment

by:kandura
ID: 12154179
don't you mean to add the directory "$source/$_" ?

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

0
 

Author Comment

by:roachfiend
ID: 12155063
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
 
LVL 18

Expert Comment

by:kandura
ID: 12155108
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
 

Author Comment

by:roachfiend
ID: 12155152
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
 
LVL 18

Accepted Solution

by:
kandura earned 215 total points
ID: 12155200
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
 

Author Comment

by:roachfiend
ID: 12155207
Yay!!! Thank you so much, it works perfectly.
0
 
LVL 84

Expert Comment

by:ozo
ID: 12155219
shouldn't
if (/<$n>(.*)<\/$n>/){
be
for( /<$n>(.*?)<\/$n>/g ){
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:roachfiend
ID: 12155229
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
 
LVL 18

Expert Comment

by:kandura
ID: 12155238
I think so:

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

should skip any file or directory named "uninstall".
0
 

Author Comment

by:roachfiend
ID: 12155258
Hmm, doesn't work.
0
 
LVL 18

Expert Comment

by:kandura
ID: 12155304
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
 

Author Comment

by:roachfiend
ID: 12155323
sub { !/uninstall/ };

worked. Thanks again kandura!
0
 
LVL 18

Expert Comment

by:kandura
ID: 12155341
You're welcome :-)

Just out of curiosity, what are you doing with those firefox extensions?
0
 

Author Comment

by:roachfiend
ID: 12155365
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
 

Author Comment

by:roachfiend
ID: 12155374
argh, misspelled my own name. that link above should be

http://extensions.roachfiend.com/howto.php
0
 
LVL 18

Expert Comment

by:kandura
ID: 12155414
neat :)
I actually use your BugMeNot extension, how cool ;)
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Many time we need to work with multiple files all together. If its windows system then we can use some GUI based editor to accomplish our task. But what if you are on putty or have only CLI(Command Line Interface) as an option to  edit your files. I…
Email validation in proper way is  very important validation required in any web pages. This code is self explainable except that Regular Expression which I used for pattern matching. I originally published as a thread on my website : http://www…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now