Assuming I have the following array of file names I wish to zip
my #files = ("C:\Windows\Perl\test1.txt", "C:\Windows\Perl\test2.txt", "C:\Windows\Perl\test3.txt");
If I do
$obj = Archive::Zip->new();
foreach (#files)
{
$obj->addFile($_);
}
$obj->writeToFileNamed("zippedFolders.zip");
When I open zippedFolders.zip I see it contains subfolders, namely Windows and Perl, the latter which actually contains test1, test2, and test3. For some reason, the folders themselves are getting zipped up.
How can I make it so that only the files get zipped up and not have to click Windows then Perl folders to access the zipped files?
As you have see, if you use addFile to add a disk file to an archive, then Archive::Zip adds an archive member with the same path as file had originally. If you want it stored as something different then you can pass a second parameter that will be used as the name and path of the archive member created.
For your purposes I suggest you use the core File::Basename module to remove the path from the filename and pass just the basename as the second parameter.
The code below demonstrates.
Something else you need to be aware of is that you can't use single backslashes in Perl double quotes - they will be seen as escaping the following character and simply disappear. You can use pairs of backslashes in the string, use single quotes instead, use forward slashes instead (Perl will sort things out) or if there are no spaces in the filenames then you can use qw() as I have in this program.
use strict;
use warnings;
use Archive::Zip;
use File::Basename 'basename';
my #files = qw/
C:\Windows\Perl\test1.txt
C:\Windows\Perl\test2.txt
C:\Windows\Perl\test3.txt
/;
my $zip = Archive::Zip->new;
foreach my $file (#files) {
my $member = basename $file;
printf qq{Adding file "%s" as archive member "%s"\n}, $file, $member;
$zip->addFile($file, $member);
}
$zip->writeToFileNamed('zippedFolders.zip');
output
Adding file "C:\Windows\Perl\test1.txt" as archive member "test1.txt"
Adding file "C:\Windows\Perl\test2.txt" as archive member "test2.txt"
Adding file "C:\Windows\Perl\test3.txt" as archive member "test3.txt"
Related
I am trying to access a text file within a zipped folder to extract a certain information, without actually unzipping the file. I am trying to use Archive::Zip. The directory structure is like Data_stats.zip--> Data_stats/ --> full_data_stats.txt. Now I tried this
use Archive::Zip;
use Archive::Zip::MemberRead;
use File::Basename;
$zip_dir=$ARGV[0];
#name =split("\\.",basename($zip_dir)); ## to get zipped folder name
$dir = Archive::Zip->new("$zip_dir");
$fh = Archive::Zip::MemberRead->new($dir,"$name[0]/full_data_stats.txt"); ##trying to reads the file giving the path and mentioning the specific file name
while (defined($line = $fh->getline()))
{
{print}
}
I see it extracting the folder but not reading in the file !!.
Regards
You are assigning to $line but printing $_; try print $line;
I'm trying to zip up image files using Archive::Zip. The files are in Data/Temp/Files When I loop through the logs in the directory and add them to the zip file, I end up with the folder hierarchy and the image files when I only want the image files.
So the zip ends up containing:
Data
└Temp
└Files
└Image1.jpg
Image2.jpg
Image3.jpg
When I want the zip file to contain is:
Image1.jpg
Image2.jpg
Image3.jpg
Here is the script I'm running to test with:
#!/usr/bin/perl
use Archive::Zip;
$obj = Archive::Zip->new(); # new instance
#files = <Data/Temp/Files/*>;
foreach $file (#files) {
$obj->addFile($file); # add files
}
$obj->writeToFileNamed("Data/Temp/Files/Images.zip");
Use chdir to change into the directory:
use Archive::Zip;
$obj = Archive::Zip->new(); # new instance
chdir 'Data/Temp/Files';
#files = <*>;
foreach $file (#files) {
$obj->addFile($file); # add files
}
$obj->writeToFileNamed("Images.zip");
The names and paths of zip archive members are completely independent of those of their real file counterparts. Although the two names are conventionally the same, AddFile allows you to specify a second parameter which is the name and path of the corresponding archive member where the file information should be stored
You can achieve the effect you're asking for my using basename from the File::Basename module to extract just the file name from the complete path
This program demonstrates. Note that it is essential to use strict and use warnings at the top of every Perl program you write
use strict;
use warnings;
use Archive::Zip;
use File::Basename 'basename';
my $zip = Archive::Zip->new;
for my $jpg ( glob 'Data/Temp/Files/*.jpg' ) {
$zip->addFile($jpg, basename($jpg));
}
$zip->writeToFileNamed('Data/Temp/Files/Images.zip');
I am creating a Perl script to deploy webcode (Windows 2008 Server). I need to first Copy all of the old files from the destination folder and create an archive dir for the files with a timestamp trailing on the archive dir name (arc_dir.20131217). Move the files into the archive. Then I need to copy the code from the source dir into the destination folder. However it is not working at all and I am absolutely clueless as to why.
Two things, I am very green with Perl as will be shortly seen and I do not want someone to do the code for me. It kind of defeats the purpose of learning. Direction and a dialogue would be great. I am a veterans dream, willing to learn and I desire to write only clean code.
use strict;
use warnings;
use autodie;
use File::Copy; #Gives you access to the "move" command
use File::Path; #Copies data recursively from one dir and creates a new dir
use POSIX;
#Set Directories
use constant {
TIMESTAMP => strftime("%Y%M%d%H%M%S", localtime);
Source_Dir => "C:\Users\Documents\Source_Dir",
destination_Dir => "C:\Users\Documents\Destination_Dir",
ARCHIVE => "C:\Users\Documents\arc_dir.TIMESTAMP",
};
#Creates new directory to archive old files
make_path('C:\Users\Shaun\Documents\arc_dir.TIMESTAMP');
#Need to copy destination dir, create archive dir and paste data to it
#Opens destination_Dir, so I can read from it
opendir my $dir, destination_Dir;
# Loop through directory and grab all of the files and store in var
while (my $file = readdir $dir) {
my $destination_Dir = destination_Dir . "/" . "$file";
move $destination_Dir, ARCHIVE;
#Loop through directory and copy all webcode to destination_Dir
opendir my $dir, Source_Dir;
while (my $file = readdir $dir) {
my $Source_Dir = Source_Dir . "/" . "$file";
move $Source_Dir, destination_Dir;
There are some syntax errors in the script. I would use the -c on the command line to the PERL to check the syntax of the script (such as perl -c ). Please make sure your () and {} are matched.
The other item is that backwards slash (\) need to be escaped with a backward slash (\) when in double quotes ("). Otherwise it is just escaping the next character in the string (and is probably not what you want for a path name). Strings in double quotes are interpolated before being processed, where single quotes are not (Nice explanation of the difference between sinqle quotes and doubles: http://www.perlmonks.org/?node_id=401006). You may want to change the sinqle quotes you have to double quotes in your makepath call. Otherwise TIMESTAMP will not be changed and the directory will have the name TIMESTAMP.
I would also suggest putting in some print statement to indicate what is being done and to give feedback that items are progressing. Such as printing the "moving $destination_Dir to ARCHIVE" and "moving $Source_Dir to destination_Dir" would let you know files are being moved.
I have a directory or folder consisting of hundreds of files. They are named and arranged alphabatically. I want to move the files into directories or folders according to the first character of their name (i.e. files starting with a into one folder, files starting with r into another folder, etc).
Is there a way to do it without using CPAN modules?
Are the files all in that one folder, or are they in subfolders? If they are all in a single folder, you can use opendir to access the directory, and then readdir to read the file names and copy them elsewhere (using File::Copy module's move or copy function.
use strict;
use warnings;
use autodie;
use File::Copy; #Gives you access to the "move" command
use constant {
FROM_DIR => "the.directory.you.want.to.read",
TO_DIR => "the.directory.you want.to.move.the.files.to",
};
#Opens FROM_DIR, ao I can read from it
opendir my $dir, FROM_DIR;
# Loopa through the directory
while (my $file = readdir $dir) {
next if ($file eq "." or $file eq "..");
my $from = FROM_DIR . "/" . "$file";
move $from, TO_DIR;
}
This doesn't do exactly what you want, but it should give you the idea. Basically, I'm using opendir and readdir to read the files in the directory and I'm using move to move them to another directory.
I used the File::Copy module, but this is included in all Perl distributions, so it's not a CPAN module that must be installed.
Use glob(), or the built-in File::Find to build a list of files for each starting letter.
I have a folder called "Lib" in my drive it contains many files inside and I have a problem that this "Lib" folder is there in many other places in the drive. My Perl script has to copy the contents from folder "Lib" which are latest updated and paste it in the folder "d:\perl\Latest_copy_of_Lib"
For example, I have a Lib folders in d:\functions, d:\abc, and many other places. I want to find the latest copy of each file in those directories. So, if the file d:\functions\foo.txt was last modified on 2009-10-12 and d:\abc\foo.txt was last modified on 2009-10-13, then I want the version in d:\abc to by copied to the target directory.
I have used file::find but it searches in whole dir and copies the contents that are not latest copy.
I think you just described rsync. Unless you have some sort of weird requirements here, I don't think you need to write any code to do this. I certainly wouldn't reach for Perl to do the job you described.
You need to use File::Find to create a hash of files to move. Only put the path to a file in the hash if the file is newer than the path already stored in the hash. Here is a simple implementation. Note, there may be problems on the windows platform, I am not used to using File::Spec to work with files and pathes in a cross platform manner.
#!/usr/bin/perl
use warnings;
use strict;
use File::Find;
use File::Spec;
my %copy;
my #sources = qw{
/Users/cowens/foo/Lib
/Users/cowens/bar/Lib
/Users/cowens/baz/Lib
};
find sub {
my ($volume, $dir, $file) = File::Spec->splitpath($File::Find::name);
my #dirs = File::Spec->splitdir($dir);
my #base = ($volume); #the base directory of the file
for my $dir (#dirs) {
last if $dir eq 'Lib';
push #base, $dir;
}
#the part that is common among the various bases
my #rest = #dirs[$#base .. $#dirs];
my $base = File::Spec->catdir(#base);
my $rest = File::Spec->catfile(#rest, $file);
#if we don't have this file yet, or if the file is newer than the one
#we have
if (not exists $copy{$rest} or (stat $File::Find::name)[9] > $copy{$rest}{mtime}) {
$copy{$rest} = {
mtime => (stat _)[9],
base => $base
};
}
}, #sources;
print "copy\n";
for my $rest (sort keys %copy) {
print "\t$rest from $copy{$rest}{base}\n";
}
If you can standardize on a single location for your libraries, and then use one of the following:
set PERL5LIB Environment variable and add
use lib 'C:\Lib';
or
perl -I C:\Lib myscript
Any of these will give you a single copy of your lib directory that any of your scripts will be able to access.