7Zip execute 2 extractions from 2 different subfolders (only first executes) - perl

Okay I'm at a total loss.
I am trying to extract all the XMLs and PDFs from a 7zip file.
There is more stuff inside said file, so I just want to extract from the PDF folder and the XML folder. Leaving the file structure behind and not searching in any other folders.
I am using the 7Zip command line to do this.
I have two sub routines that I execute which are almost identical.
sub Extract_pdfs_from_this
{
my ($file, $destination) = #_;
my $sevenzip_executable = '\\\\server\7-Zip\7z.exe';
my $extract_pdfs = "$sevenzip_executable e -y -o$destination $file output\\JETPDF\\DISB\\*.pdf ";
print STDOUT "\n\nExtracting PDFs From $file \n>>$extract_pdfs \n";
eval{system($extract_pdfs)};
print STDOUT "Finished Extracting PDFs \n";
return;
}
..
sub Extract_xmls_from_this
{
my ($file, $destination) = #_;
my $sevenzip_executable = '\\\\server\7-Zip\7z.exe';
my $extract_xmls = "$sevenzip_executable e -y -o$destination $file staging\\DISB\\OnBase\\*.xml ";
print STDOUT "\n\nExtracting XMLs From $file \n>>$extract_xmls \n";
eval{system($extract_xmls)};
print STDOUT "Finished Extracting XMLs \n";
return;
}
and I use it like so...
my $in_extraction_directory = dirname(__FILE__);
my $input_subdirectory = "$directory\\$subdirectory";
my #in_seven_zip_files = Get_all_sevenzips_in($input_subdirectory);
foreach my $sevenzip_file (#in_seven_zip_files)
{
$sevenzip_file = "$input_subdirectory\\$sevenzip_file";
Extract_pdfs_from_this($sevenzip_file, $in_extraction_directory);
Extract_xmls_from_this($sevenzip_file, $in_extraction_directory);
}
When executed the PDFs get extracted but not the XMLs.
I get an error, there are no files to process.
I feel like 7zip is hung up on the file from the previous call. Is there a way to close it or release the file?
Any help appreciated, much time wasted on this.
Thanks!

Check exit status $?, if you feel it's hung.
Also you can try first extracting xmls then pdfs to really make sure, if extracting pdfs command is making issue.
share console output, Which can show much details.

User error... Works just how it should.
I had a condition:
unless ($number_of_pdfs == $number_of_xmls)
{
print STDOUT "The number of PDFs and XMLs did not match!\n\n";
print STDOUT "PDFs: $number_of_pdfs \nXMLs: $number_of_xmls\nFile: $sevenzip_file \nExtraction Directory: $output_directory\n\n";
die;
}
and in the first file I was extracting, the XML was not in the correct path... Someone didn't follow pattern. Very embarrassing thanks for the response.

Related

Is there any way to check whether a file is an empty file using Perl program?

I am looking a small script which will tell me whether a file is an empty file or not, but I am unable to display this.
I have used the below code:
opendir DIR,$directory ;
while (my $dir =readdir DIR) {
if (-s "$dir") {
print "This is an empty file";
}
}
Here I am unable to print "This is an empty file" and my code does not went inside the if loop. Can anyone tell me what is the wrong in the above code?
The relevant file test operators are:
-z: check if the file is empty.
-s: check if the file has nonzero size (returns size in bytes).
You are checking if $dir is non-empty, so opposite of what you are trying to achieve. Use -z (or !-s) instead.
Also, each $dir is just the filename without the path, so you need to include it yourself if you aren't processing the current directory.
if (-z "$directory/$dir") {
print "This is an empty file";
}
Your main problem is that readdir() doesn't return what you think it does. It returns the names of the files in the directory. But the names you get back don't have the directory path attached. And the -s operator needs the full path to the file in order to find it.
So you need to attach the directory name yourself. Something like this will work:
opendir DIR, $directory ;
while (my $dir = readdir DIR) {
# Note: Full path here.
if (-s "$directory/$dir") {
print "This is an empty file";
}
}
But there are a couple of other things I'll fix. Firstly -s returns the size of the file. So that's true for a non-empty file. Which means that your logic is reversed. It actually needs to be this:
if (-s "$directory/$dir") {
print "This is a non-empty file";
} else {
print "This is an empty file";
}
It's also worth noting that using bareword directory handles isn't a very good idea. It's far better to use lexical variables as handles. I'd write your code like this:
opendir my $dir_h, $directory ;
while (my $file = readdir $dir_h) {
# Note: Full path here.
if (-s "$directory/$file") {
print "This is a non-empty file";
} else {
print "This is an empty file";
}
}
Oh, I also sneakily changed a variable name. $file seems a far better name thant $dir :-)

Find out in Perl and Windows if a file is writeable/ removable

I would like to build in Perl under Windows a Watch-Dog for a Hot-Folder (I might call it Folder-Watch or, hmm, probably much better: a Hot-Dog).
So far I succeeded in exactly doing that, with Win32::ChangeNotify (see sample below).
But as you might guess reading the source code I ran into a problem when the moving process wants to finish when the copying/creating process of the file in $watchdir has not finished (No such file or directory).
use Win32::ChangeNotifier;
use File::Copy qw(move);
my $notify = Win32::ChangeNotify->new($watchdir, 0, "FILE_NAME");
while (1) {
if ($notify->wait(1_000)) { # 1-second wait cycle
notify->reset;
#foundfiles = File::get_by_ext($watchdir, "csv"); # search and return files in $watchdir with extension "csv"
print "Something has happened! (del/ren/create)\n";
foreach (#foundfiles) {
move($watchdir.$_, $someotherdir.$_) or die "Fehler: $!";
}
#foundfiles = ();
}
}
Is there a way to automatically find out if the file is ready to go, i.e. has been finally created/copied?
I was thinking about something like
while (1) {
move $file if (-w $file) # writeable
wait(1)
}
but that does not seem to work under Windows. I need to solve this under Windows as well as Perl. Other than that I am open to suggestions.
Yes! I solved it (thanks to Сухой27)!
Inserting the following code right before moving the file:
while (1) {
last if writeable($path_in.$_);
print "-";
$| = 1;
sleep(1);
}
...whereas writeable refers to this little sub-marine:
sub writeable {
return open(my $file, ">>", shift);
}
Thanks, and have a nive day! :-)

perl script to parse log files from different test locations.which takes dynamic path of testcases

I want a perl script that will go in to every test folder and parse the log file in it.
Eg:
results/testcases/ **?** /test.log
The above path must be dynamically changing with different test folder names in the place of **?** mark.
I am using this results/testcases/#array/test.log
#array has test names
My suggestion would be:
my $path = "results/testcases";
opendir(TEMPDIR,$path) or die "err1";
my #dir = grep -d, readdir TEMPDIR;
foreach(#dir)
{
if( $_ !~ /\./ )
{
open( my $fileHandle , "results/testcases/".$_."/test.log" ) or die "err2";
# parsing log file
close $fileHandle or die "err2-2";
}
}
close TEMPDIR or die "err1-2";
First, you need to read the folder "results/testcases" for current correct folder names. Second, you need to open the files one by one, stead of putting #array in the middle of the path. Third, you should read basic perl, otherwise you won't be able to parse in a proper manner. Fourth, you really should read through HOW TO ASK, you should put in your code so that we could be more helpful and your questions shall help others, as well.
If your test folders relative paths are stored in #array. You can do the following:
my #testlogs = grep { -e $_ } map { "results/testcases/".$_."/test.log" } #array;
The new array #testlogs now contains the list of paths to existing 'test.log' files.
Then, you can parse each file like this:
map { ... parsing call ... } #testlogs;

Creating a file on the fly and return on the fly (Perl)

I have to create a file based on user inputs on the server. Return that file.
Now, I don't want the files once the file is returned to the user (otherwise they will fill up my disk space).
Is there a good way of doing this?
I have to do it in Perl.
Thank You.
If you're building a zip file and immediately sending it back to the client then you have lots of options.
You could use writeToFileHandle combined with IO::Scalar to write the zip file data straight to a string and send that back. This approach avoids the whole file issue completely.
You could also use writeToFileHandle combined with File::Temp to write to a temporary file and then stream that file back. The UNLINK option for File::Temp will help you automatically clean up the temp file as well.
This is what I created and works for me. When user opening some URL on my page I am creating ZIP archive on the fly and sending it to user.
If change "-" to "file.zip" it will save archive into your hosting/server (and don't need print "content type").
Tried with big size files (400-500Mb each file) - all ok.
use IO::Compress::Zip qw(:all);
my #files = ('example.gif', 'example1.png', 'example2.jpg', 'example3.avi', 'example4.mov');
my $path = "/home/********/**********";
print "Content-Type:application/zip\n";
print "Content-Disposition: attachment; filename=\"filename.zip\"\n\n";
my $z;
foreach my $file (#files) {
if ($z) {
$z->newStream(Name => $file, Method => ZIP_CM_STORE);
} else {
$z = new IO::Compress::Zip "-", Name => $file, Method => ZIP_CM_STORE;
}
open(FILE, "<", "$path/$file");
binmode FILE;
my ($buf, $data, $n);
while (($n = read FILE,$data, 1024) != 0) {
$z->print($data);
}
close(FILE);
}
$z->close;
exit;

Archive tar files to a different location in Perl

I am reading a directory having some archive files and uncompressing the archive files one by one.
Everything seems well however the files are getting uncompressed in the folder which has the main perl code module which is running the sub modules.
I want the archive to be generated in the folder I specify.
This is my code:
sub ExtractFile
{
#Read the folder which was copied in the output path recursively and extract if any file is compressed
my $dirpath = $_[0];
opendir(D, "$dirpath") || die "Can't open dir $dirpath: $!\n";
my #list = readdir(D);
closedir(D);
foreach my $f (#list)
{
print " \$f = $f";
if(-f $dirpath."/$f")
{
#print " File in directory $dirpath \n ";#is \$f = $f\n";
my($file_name, $file_dirname,$filetype)= fileparse($f,qr{\..*});
#print " \nThe file extension is $filetype";
#print " \nThe file name is is $file_name";
# If compressed file then extract the file
if($filetype eq ".tar" or $filetype eq ".tzr.gz")
{
my $arch_file = $dirpath."/$f";
print "\n file to be extracted is $arch_file";
my $tar = Archive::Tar->new($arch_file);
#$tar->extract() or die ("Cannot extract file $arch_file");
#mkdir($dirpath."/$file_name");
$tar->extract_file($arch_file,$dirpath."/$file_name" ) or die ("Cannot extract file $arch_file");
}
}
if(-d $dirpath."/$f")
{
if($f eq "." or $f eq "..")
{ next; }
print " Directory\n";# is $f";
ExtractFile($dirpath."/$f");
}
}
}
The method ExtractFile is called recursively to loop all the archives.
When using $tar->extract() it uncompresses in the folder which calls this method.
When I use $tar->extract_file($arch_file,$dirpath."/$file_name") I get an error :
No such file in archive: '/home/fsang/dante/workspace/output/s.tar' at /home/fsang/dante/lib/Extraction.pm line 80
Please help I have checked that path and input output there is no issue with it.
Seems some usage problem I am not aware of for $tar->extract_file().
Many thanks for anyone resolving this issue.
Regards,
Sakshi
You've misunderstood extract_file. The first parameter is the name of a file inside the archive to extract. You're passing in the path of the archive itself. You passed that to new; you don't need to pass it in again. As the error message explains, s.tar does not contain a file named /home/fsang/dante/workspace/output/s.tar, so extract_file fails.
You can get a list of files in the archive by using $tar->list_files.
A simpler solution may be to temporarily chdir to the directory you want to extract the archive into. File::pushd provides an easy way to do that.
a typo?
$tar->extract_file($arch_file,$dirpath."/$file_name" )
should probably be
$tar->extract_file($arch_file,$dirpath."/".$file_name)
$tarFile = "test.tar.gz";
$myTar = Archive::Tar->new($tarFile);
foreach my $member ($myTar->list_files())
{
my $res = $myTar->extract_file( $member , 'C:/temp/'.$member );
print "Exract error!\n" unless ($res);
}
I see a gun being brought to a Swiss Army knife fight.
Here's a *nix one-liner that does what you want:
find /source/dir -name "*.tar" -exec tar -C /target/dir -xvzf '{}' \; -print
Is there a need to write a script for this?
You aren't necessarily doing anything special other than debug lines.