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;
Related
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 have placed the text file "FilenameKeyword.txt" file in E:/Test folder, in my perl script i am trying to traverse through the folder and am i am trying to find a file with filename which has the string "Keyword" in it, later i have printed the content of that file in my script.
Now i wish do the same thing for the file which is placed inside tar file which is compressed.
Hypothetical File from where i am trying to extract the details:
E:\test.tar.gz
Wanted to know if there are possibility in perl to search and read the file without decompressing /unzipping the hypothetical file.If that is not possible, I shall also allocate some temperory memory to decompress the file , which should deleted after extracting the content from the particular text file.
While Searching in the internet i could it is possible to extract and read the gzip/tar file by using Archive::Extract, being new to Perl - i am really confused on how actually i should make use of it. Could you please help on this....
Input file:FilenameKeyword.txt
Script:
use warnings;
use strict;
my #dirs = ("E:\\Test\\");
my %seen;
while (my $pwd = shift #dirs) {
opendir(DIR,"$pwd") or die "Cannot open $pwd\n";
my #files = readdir(DIR);
closedir(DIR);
foreach my $file (#files)
{
if (-d $file and ($file !~ /^\.\.?$/) and !$seen{$file})
{
$seen{$file} = 1;
push #dirs, "$pwd/$file";
}
next if ($file !~ /Keyword/i);
my $mtime = (stat("$pwd/$file"))[9];
print "$pwd$file";
print "\n";
open (MYFILE, "$pwd$file");
while (my $line = <MYFILE>){
#print $line;
my ($date) = split(/,/,$line,2);
if ($line =~ s!<messageText>(.+?)</messageText>!!is){
print "$1";
}
}
}
}
Output(In test program file is placed under E:\Test):
E:\Test\FilenameKeyword.txt
1311 messages Picked from the Queue.
Looking for help to retrieve the content of the file which is place under
E:\test.tar.gz
Desired Output:
E:\test.tar.gz\FilenameKeyword.txt
1311 messages Picked from the Queue.
I was stuck in using CPAN module, CPAN module didn't work for me as i have oracle 10g enterprise edition in the same machine, due do some software conflict Active state perl was unable compile and refer to the perl lib for CPAN module, i have uninstalled oracle in my machine to make this work....
#!/usr/local/bin/perl
use Archive::Tar;
my $tar = Archive::Tar->new;
$tar->read("test.tar.gz");
$tar->extract();
If your file was gzipped only, you could read its contents in a "streamed" manner as outlined here (Piping to/from a child process without system or backtick - gzipped tar files). The article illustrates a technique to use open and a fork to open and decompress the file, and then making it available to Perl's while(), allowing you to iterate over it.
As tar is basically concatenating things, it might be possible to adapt this to your scenario.
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"
I want to uncompress zipped file say, files.zip, to a directory that is different from my working directory.
Say, my working directory is /home/user/address and I want to unzip files in /home/user/name.
I am trying to do it as follows
#!/usr/bin/perl
use strict;
use warnings;
my $files= "/home/user/name/files.zip"; #location of zip file
my $wd = "/home/user/address" #working directory
my $newdir= "/home/user/name"; #directory where files need to be extracted
my $dir = `cd $newdir`;
my #result = `unzip $files`;
But when run the above from my working directory, all the files get unzipped in working directory. How do I redirect the uncompressed files to $newdir?
unzip $files -d $newdir
Use Perl command
chdir $newdir;
and not the backticks
`cd $newdir`
which will just start a new shell, change the directory in that shell, and then exit.
Though for this example, the -d option to unzip is probably the simplest way to do what you want (as mentioned by ennuikiller), for other types of directory-changing, I like the File::chdir module, which allows you to localize directory changes, when combined with the perl "local" operator:
#!/usr/bin/perl
use strict;
use warnings;
use File::chdir;
my $files= "/home/user/name/files.zip"; #location of zip file
my $wd = "/home/user/address" #working directory
my $newdir= "/home/user/name"; #directory where files need to be extracted
# doesn't work, since cd is inside a subshell: my $dir = `cd $newdir`;
{
local $CWD = $newdir;
# Within this block, the current working directory is $newdir
my #result = `unzip $files`;
}
# here the current working directory is back to what it was before
You can also use the Archive::Zip module. Look specifically at the extractToFileNamed:
"extractToFileNamed( $fileName )
Extract me to a file with the given name. The file will be created with default modes. Directories will be created as needed. The $fileName argument should be a valid file name on your file system. Returns AZ_OK on success. "
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.