How to check if a zip fie is empty using perl - perl

I'm writing a Perl scipt that unzips the zip file and moves the content of zip file to a directory, however I want to skip the zip file which do not have any content in it. How can I filter these files. For unzipping the file I'm using
unzip_content = system("unzip -d <directory> -j -a <filepath>")
Could anyone suggest me anyway to check if it does not contain anything.
I tried with checking the filesize using -s $filename but later I got some files with filesize 22 byte but with no content in it.

You can use Archive::Zip to achieve all of that inside of your Perl program without shelling out. You need to check if the archive contains anything, which can be done with the numberOfMembers method.
use strict;
use warnings;
use Archive::Zip qw/:ERROR_CODES/;
my #files = ...;
foreach my $file (#files) {
my $zip = Archive::Zip->new;
# skip if archive cannot be opened
next unless $zip->read($file) == AZ_OK;
# skip if archive is empty
next unless $zip->numberOfMembers;
# extract everything
$zip->extractTree({zipName => '<directory>'});
}

An other way to check the same thing is to calculate the MD5 hash of the file. MD5 hash of all empty zip files would be 76cdb2bad9582d23c1f6f4d868218d6c.
Helpful links:
minimum size zip file
Digest::MD5
use strict;
use warnings;
use Digest::MD5 qw(md5 md5_hex md5_base64);
my $filedir = "/home/docs/file.zip";
open FILE, "$filedir";
my $ctx = Digest::MD5->new;
$ctx->addfile (*FILE);
my $hash = $ctx->hexdigest;
close (FILE);
if($hash eq '76cdb2bad9582d23c1f6f4d868218d6c')
{
# empty file
}

Related

How to copy zipped file data to one txt file

Now i need to copy data inside the zip files to one .txt file i.e all R1 folder file data should copy and save in one R1.txt file similarly R2 folder file data should save in one single R2.txt file. is it possible to copy data from zipped files??
#!/usr/bin/perl
use File::Copy;
use strict;
use warnings;
print"Enter Folder name \n";
print"File name: ";
chomp(my $Filename=<>);
mkdir "R1";
mkdir "R2";
opendir(DIR,"$Filename") or die "cannot open directory";
foreach my $name (readdir(DIR))
{
next if ($name =~ /^\./);
if($name =~ /R1/) { #compare $name not $Filename
copy("$Filename/$name", "R1"); # copy the file from folder to R1 directory
system("cat $Filename\/$name >> R1.txt");
}
elsif($name =~ /R2/){
copy("$Filename/$name","R2"); ## copy the file from folder to R2 directory
system("cat $Filename\/$name >> R2.txt");
}
}
thanks in advance.
without unzipping the file you can extract the content, the simple and easiest way to do so without using modules is, to use unix command in perl
use strict;
use warnings;
my $text = `unzip -c customer.xml.gz`;
print $text ."\n";
AFter extracting the contents, write the same into one.txt file

How to zip only files and not the full path

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');

How to read a file which is gzipped and tar in perl

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.

Redirect unzip output to particular directory using Perl

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. "

How can I sync two directories with Perl?

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.