Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

How can I zip up certain files and directories recursively?

Posted on 2004-09-25
18
Medium Priority
?
585 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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 860 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
 

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

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I've just discovered very important differences between Windows an Unix formats in Perl,at least 5.xx.. MOST IMPORTANT: Use Unix file format while saving Your script. otherwise it will have ^M s or smth likely weird in the EOL, Then DO NOT use m…
I have been pestered over the years to produce and distribute regular data extracts, and often the request have explicitly requested the data be emailed as an Excel attachement; specifically Excel, as it appears: CSV files confuse (no Red or Green h…
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…
Six Sigma Control Plans

636 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