Perl script not writing to extracted zip file - perl

I'm writing a perl script to extract a specifically named file from a zip file and write to that extracted file (there's more than that to the script but this is the part that I'm getting stuck on). The code below is a portion of the perl script.
I cannot figure out why it's not writing to the file!
my $zipName = "f:\\data\\archive.zip";
my $destPath = "f:\\data\\dataFile.DAT";
my $tempZip = Archive::Zip->new();
my $dataNum = " 0 0";
unless ($tempZip->read($zipName) == AZ_OK )
{
die 'read error';
}
my #dataFileMatches = $tempZip->membersMatching( 'dataFile.*\.DAT' );
my $dataFile;
if($#dataFileMatches> -1)
{
$dataFile = $dataFileMatches[0];
my $fileContents = $tempZip->contents($dataFile);
my $newContents = substr $fileContents, 0, 10;
$newContents = $newContents.$dataNum;
my $dataFilename = $dataFile ->fileName();
open(my $fh, '>', $dataFilename) or die "Could not open file '$dataFilename' $!";
$tempZip->contents($dataFile, $newContents);
$fileContents = $tempZip->contents($dataFile);
print "New FileContents - $fileContents\n";
#print $fh $newContents;
#copy($fh, $destPath);
close $fh;
}
Here is the code that is not being executed:
use strict;
use warnings;
use File::Copy qw(copy);
use Archive::Zip qw/ :ERROR_CODES :CONSTANTS /;
my $filename = 'dataFile.DAT';
open(my $fh, '>', $filename) or die "Could not open file";
print $fh "test\n";
close $fh;
But if I create a dummy TEXT file in the exact same directory that this extracted DAT file is in, it works.

You have to use the extractMemberWithoutPaths() method of Archive::Zip.
Here is how to extract a file:
#!perl
use strict;
use warnings;
use Data::Dumper qw/Dumper/;
use FindBin qw/$Bin/;
use File::Spec;
use Archive::Zip qw/AZ_OK/;
my $zipName = File::Spec->catfile($Bin, '/archive.zip');
my $destPath = File::Spec->catfile($Bin, 'dataFile.DAT');
my $tempZip = Archive::Zip->new();
my $dataNum = " 0 0";
unlink $destPath if -e $destPath;
unless ($tempZip->read($zipName) == AZ_OK ) {
die 'read error';
}
my #dataFileMatches = $tempZip->membersMatching( {regex => 'dataFile.*\.DAT'} );
$tempZip->extractMemberWithoutPaths($dataFileMatches[0]); # extract $fileContents to current working directory
if ( -e $destPath ) {
print "file was extracted\n";
}else{
print "File was not extracted :(\n";
}

Related

Read multiple files from folder in perl

I'm pretty new on perl and in need for some help, basically what I want is a program that reads all .txt files from a folder, doing the script and throw the output in a new folder with a new name. Everything works when I'm working with one file at the time, specifying the name of the file.. But I can't get it to work with all of the files in the folder. This is how far I've gotten.
#!/usr/bin/perl
use warnings;
use strict;
use Path::Class;
use autodie;
use File::Find;
my #now = localtime();
my $timeStamp = sprintf(
"%04d%02d%02d-%02d:%02d:%02d",
$now[5] + 1900,
$now[4] + 1,
$now[3], $now[2], $now[1], $now[0]); #A function that translates time
my %wordcount;
my $dir = "/home/smenk/.filfolder";
opendir(DIR, $dir) || die "Kan inte öppna $dir: $!";
my #files = grep { /txt/ } readdir(DIR);
closedir DIR;
my $new_dir = dir("/home/smenk/.result"); # Reads in the folder for save
my $new_file = $new_dir->file("$timeStamp.log"); # Reads in the new file timestamp variable
open my $fh, '<', $dir or die "Kunde inte öppna '$dir' $!";
open my $fhn, '>', $new_file or die "test '$new_file'";
foreach my $file (#files) {
open(FH, "/home/smenk/.filfolder/$file") || die "Unable to open $file - $!\n";
while (<FH>) {
}
close(FH);
}
while (my $line = <$fh>) {
foreach my $str (split /\s+/, $line) {
$wordcount{$str}++;
}
}
my #listing = (sort { $wordcount{$b} <=> $wordcount{$a} } keys %wordcount)[0 .. 9];
foreach my $str (#listing) {
my $output = $wordcount{$str} . " $str\n";
print $fhn $output;
}
Here is the simplest skeleton for the reading part using Path::Class (see also dir and file:
#!/usr/bin/perl
use warnings;
use strict;
use Path::Class;
my $src = dir("/home/smenk/.filfolder");
my #txt_files = grep /[.] txt\z/x, $src->children;
for my $txt_file ( #txt_files ) {
my $in = $txt_file->openr;
while (my $line = <$in>) {
print "OUT: $line";
}
}
You can also use another great module Path::Tiny, for dir/file operations and the Time::Piece for the date/time functions - like:
#!/usr/bin/env perl
use strict;
use warnings;
use Path::Tiny;
use Time::Piece;
my #txtfiles = path("/home/smenk/.filfolder")->children(qr/\.txt\z/);
my $outdir = path("home/smenk/.result");
$outdir->mkpath; #create the dir...
my $t = localtime;
my $outfile = $outdir->child($t->strftime("%Y%m%d-%H%M%S.txt"));
$outfile->touch;
my #outdata;
for my $infile (#txtfiles) {
my #lines = $infile->lines({chomp => 1});
#do something with lines and create the output #data
push #outdata, scalar #lines;
}
$outfile->append({truncate => 1}, map { "$_\n" } #outdata); #or spew;

Modify Perl script to run for each file with specified extension in a given directory

I need to modify a Perl script x937.pl to run on all files with extension .x937 within a specific directory. Currently, I use a separate script test.pl that calls my main script, and runs it for each file of that type. However, I need to combine both into one script.
Ideally, I would be able to specify a directory path in the script, and loop through all *.x937 files in that directory.
test.pl
#!/usr/bin/perl -w
use strict;
use Encode;
my #files = <*.x937>;
foreach my $file (#files) {
system('x937.pl', $file);
}
x937.pl
#!/usr/bin/perl -w
use strict;
use Encode;
use warnings;
my $tiff_flag = 0;
my $count = 0;
my $file = "output_$ARGV[0].txt";
unless ( open OPUT, '>' . $file ) {
die "Unable to create $file";
}
open FILE, '<:raw', $ARGV[0] or die "Error opening '$ARGV[0]' $!";
binmode( FILE ) or die 'Error setting binary mode on input file';
while ( read( FILE, $_, 4 ) ) {
my $rec_len = unpack( "N", $_ );
die "Bad record length: $rec_len" unless ( $rec_len > 0 );
read( FILE, $_, $rec_len );
if ( substr( $_, 0, 2 ) eq "\xF5\xF2" ) {
if ( $tiff_flag ) {
$count++;
open( TIFF, '>', 'output_' . $ARGV[0] . '_img' . sprintf( "%04d", $count ) . '.tiff' )
or die "Can't create image file $!";
binmode( TIFF ) or die 'Error setting binary mode on image file';
print TIFF substr( $_, 117 );
close TIFF;
}
$_ = substr( $_, 0, 117 );
}
print OPUT decode( 'cp1047', $_ ) . "\n";
}
close FILE;
close OPUT;
I think I managed to generate this correctly (on iPad, sat on the sofa) ... There could be some typos ; )
Usage: perl test_x397.pl <path>
test_x397.pl
#!/usr/bin/perl -w
use strict; use warnings;
use Encode;
my ($path) = #ARGV;
$path // die "No path specified";
(-e $path) or die "Path not found: $path";
(-d $path) or die "Not a directory: $path";
my #files = <$path/*.x937>;
foreach my $file (#files) {
process($file);
}
sub process {
my ($fname) = #_;
my ($dir, $file) = $fname =~ m{^(.*)/(.+)$};
my $tiff_flag = 0;
my $count = 0;
my $outfile = sprintf("%s/output_%s.txt", $dir, $file);
open (my $outfh, '>', $outfile) or die "Unable to create $outfile. $!";
open (my $infh, '<:raw', $file) or die "Error opening '$file'. $!";
my $buffer = undef;
while (read ($infh,$buffer,4)) {
my $rec_len = unpack("N", $buffer);
die "Bad record length: $rec_len" unless ($rec_len > 0);
read ($infh, $buffer, $rec_len);
if (substr($buffer, 0, 2) eq "\xF5\xF2") {
if ($tiff_flag) {
$count++;
my $tiff_filename = sprintf('%s/output_%s_img%04d.tiff', $dir, $file, $count);
open (my $tiffh, '>', $tiff_filename) or die "Can't create image file $!";
binmode($tiffh) or die 'Error setting binary mode on image file';
print $tiffh substr($buffer, 117);
close $tiffh;
}
$buffer = substr($buffer, 0, 117);
}
print $outfh decode ('cp1047', $buffer) . "\n";
}
close $infh;
close $outfh;
}
A few things to note:
Always use the three argument version of open
Using a scalar filehandle makes it easier to pass it around (not necessary in this example but good practice)
Don't modify $_. It can lead to nasty surprises in larger programs
You already used sprintf to make part of your tiff filename, so why not use it for the whole thing.

How to read file in Perl and if it doesn't exist create it?

In Perl, I know this method :
open( my $in, "<", "inputs.txt" );
reads a file but it only does so if the file exists.
Doing the other way, the one with the +:
open( my $in, "+>", "inputs.txt" );
writes a file/truncates if it exists so I don't get the chance to read the file and store it in the program.
How do I read files in Perl considering if the file exists or not?
Okay, I've edited my code but still the file isn't being read. The problem is it doesn't enter the loop. Anything mischievous with my code?
open( my $in, "+>>", "inputs.txt" ) or die "Can't open inputs.txt : $!\n";
while (<$in>) {
print "Here!";
my #subjects = ();
my %information = ();
$information{"name"} = $_;
$information{"studNum"} = <$in>;
$information{"cNum"} = <$in>;
$information{"emailAdd"} = <$in>;
$information{"gwa"} = <$in>;
$information{"subjNum"} = <$in>;
for ( $i = 0; $i < $information{"subjNum"}; $i++ ) {
my %subject = ();
$subject{"courseNum"} = <$in>;
$subject{"courseUnt"} = <$in>;
$subject{"courseGrd"} = <$in>;
push #subjects, \%subject;
}
$information{"subj"} = \#subjects;
push #students, \%information;
}
print "FILE LOADED.\n";
close $in or die "Can't close inputs.txt : $!\n";
Use the proper test file operator:
use strict;
use warnings;
use autodie;
my $filename = 'inputs.txt';
unless(-e $filename) {
#Create the file if it doesn't exist
open my $fc, ">", $filename;
close $fc;
}
# Work with the file
open my $fh, "<", $filename;
while( my $line = <$fh> ) {
#...
}
close $fh;
But if the file is new (without contents), the while loop won't be processed. It's easier to read the file only if the test is fine:
if(-e $filename) {
# Work with the file
open my $fh, "<", $filename;
while( my $line = <$fh> ) {
#...
}
close $fh;
}
You can use +>> for read/append, creates the file if it doesn't exist but doesn't truncate it:
open(my $in,"+>>","inputs.txt");
First check whether the file exists or not. Check the sample code below :
#!/usr/bin/perl
use strict;
use warnings;
my $InputFile = $ARGV[0];
if ( -e $InputFile ) {
print "File Exists!";
open FH, "<$InputFile";
my #Content = <FH>;
open OUT, ">outfile.txt";
print OUT #Content;
close(FH);
close(OUT);
} else {
print "File Do not exists!! Create a new file";
open OUT, ">$InputFile";
print OUT "Hello World";
close(OUT);
}

Archive::Tar : can't read tar created just before in Perl

I'm trying to read the content of a file (a.txt) that I just created. This file contains a string consisting of "ABCDE", which I then tar with the write() function. I can see that the "a.tar" is created in my directory, but when I come up to the read() function, I get an error : Can't read a.tar : at testtar.pl line 14.
Am I doing something wrong ? Is it because I am on Windows ?
use strict;
use warnings;
use Archive::Tar;
# $Archive::Tar::DO_NOT_USE_PREFIX = 1;
my $tari = Archive::Tar->new();
$tari->add_files("a.txt");
$tari->write("a.tar");
my $file = "a.tar";
my $tar = Archive::Tar->new() or die "Can't create a tar object : $!";
if(my $error = $tar->read($file)) {
die "Can't read $file : $!";
}
my #files = $tar->get_files();
for my $file_obj(#files) {
my $fh = $file_obj->get_content();
binmode($fh);
my $fileName = $file_obj->full_path();
my $date = $file_obj->mtime();
print $fh;
}
Thanks.
You misunderstand the return value of read of Archive::Tar:
$tar->read ( $filename|$handle, [$compressed, {opt => 'val'}] )
Returns the number of files read in scalar context, and a list of Archive::Tar::File objects in list context.
Please change the following
if(my $error = $tar->read($file)) {
die "Can't read $file : $!";
}
to
unless ($tar->read($file)) {
die "Can't read $file : $!";
}
and try again.
This is wrong:
my $fh = $file_obj->get_content();
binmode($fh);
get_content() gives you the contents of the file and not a filehandle. binmode() is expecting a filehandle. Also, you can use !defined instead of unless (I think it's easier to read).
Rewritten below:
#!/bin/env perl
use strict;
use warnings;
use Archive::Tar;
my $tari = Archive::Tar->new();
$tari->add_files("a.txt");
$tari->add_files("b.txt");
$tari->add_files("c.txt");
$tari->add_files("d.txt");
$tari->write("a.tar");
my $file = "a.tar";
my $tar = Archive::Tar->new() or die "Can't create a tar object : $!";
if(!defined($tar->read($file)))
{
die "Can't read $file : $!";
}
my #files = $tar->get_files();
for my $file_obj(#files)
{
my $fileContents = $file_obj->get_content();
my $fileName = $file_obj->full_path();
my $date = $file_obj->mtime();
print "Filename: $fileName Datestamp: $date\n";
print "File contents: $fileContents";
print "-------------------\n";
}

perl + read multiple csv files + manipulate files + provide output_files

Apologies if this is a bit long winded, bu i really appreciate an answer here as i am having difficulty getting this to work.
Building on from this question here, i have this script that works on a csv file(orig.csv) and provides a csv file that i want(format.csv). What I want is to make this more generic and accept any number of '.csv' files and provide a 'output_csv' for each inputed file. Can anyone help?
#!/usr/bin/perl
use strict;
use warnings;
open my $orig_fh, '<', 'orig.csv' or die $!;
open my $format_fh, '>', 'format.csv' or die $!;
print $format_fh scalar <$orig_fh>; # Copy header line
my %data;
my #labels;
while (<$orig_fh>) {
chomp;
my #fields = split /,/, $_, -1;
my ($label, $max_val) = #fields[1,12];
if ( exists $data{$label} ) {
my $prev_max_val = $data{$label}[12] || 0;
$data{$label} = \#fields if $max_val and $max_val > $prev_max_val;
}
else {
$data{$label} = \#fields;
push #labels, $label;
}
}
for my $label (#labels) {
print $format_fh join(',', #{ $data{$label} }), "\n";
}
i was hoping to use this script from here but am having great difficulty putting the 2 together:
#!/usr/bin/perl
use strict;
use warnings;
#If you want to open a new output file for every input file
#Do it in your loop, not here.
#my $outfile = "KAC.pdb";
#open( my $fh, '>>', $outfile );
opendir( DIR, "/data/tmp" ) or die "$!";
my #files = readdir(DIR);
closedir DIR;
foreach my $file (#files) {
open( FH, "/data/tmp/$file" ) or die "$!";
my $outfile = "output_$file"; #Add a prefix (anything, doesn't have to say 'output')
open(my $fh, '>', $outfile);
while (<FH>) {
my ($line) = $_;
chomp($line);
if ( $line =~ m/KAC 50/ ) {
print $fh $_;
}
}
close($fh);
}
the script reads all the files in the directory and finds the line with this string 'KAC 50' and then appends that line to an output_$file for that inputfile. so there will be 1 output_$file for every inputfile that is read
issues with this script that I have noted and was looking to fix:
- it reads the '.' and '..' files in the directory and produces a
'output_.' and 'output_..' file
- it will also do the same with this script file.
I was also trying to make it dynamic by getting this script to work in any directory it is run in by adding this code:
use Cwd qw();
my $path = Cwd::cwd();
print "$path\n";
and
opendir( DIR, $path ) or die "$!"; # open the current directory
open( FH, "$path/$file" ) or die "$!"; #open the file
**EDIT::I have tried combining the versions but am getting errors.Advise greatly appreciated*
UserName#wabcl13 ~/Perl
$ perl formatfile_QforStackOverflow.pl
Parentheses missing around "my" list at formatfile_QforStackOverflow.pl line 13.
source dir -> /home/UserName/Perl
Can't use string ("/home/UserName/Perl/format_or"...) as a symbol ref while "strict refs" in use at formatfile_QforStackOverflow.pl line 28.
combined code::
use strict;
use warnings;
use autodie; # this is used for the multiple files part...
#START::Getting current working directory
use Cwd qw();
my $source_dir = Cwd::cwd();
#END::Getting current working directory
print "source dir -> $source_dir\n";
my $output_prefix = 'format_';
opendir my $dh, $source_dir; #Changing this to work on current directory; changing back
for my $file (readdir($dh)) {
next if $file !~ /\.csv$/;
next if $file =~ /^\Q$output_prefix\E/;
my $orig_file = "$source_dir/$file";
my $format_file = "$source_dir/$output_prefix$file";
# .... old processing code here ...
## Start:: This part works on one file edited for this script ##
#open my $orig_fh, '<', 'orig.csv' or die $!; #line 14 and 15 above already do this!!
#open my $format_fh, '>', 'format.csv' or die $!;
#print $format_fh scalar <$orig_fh>; # Copy header line #orig needs changeing
print $format_file scalar <$orig_file>; # Copy header line
my %data;
my #labels;
#while (<$orig_fh>) { #orig needs changing
while (<$orig_file>) {
chomp;
my #fields = split /,/, $_, -1;
my ($label, $max_val) = #fields[1,12];
if ( exists $data{$label} ) {
my $prev_max_val = $data{$label}[12] || 0;
$data{$label} = \#fields if $max_val and $max_val > $prev_max_val;
}
else {
$data{$label} = \#fields;
push #labels, $label;
}
}
for my $label (#labels) {
#print $format_fh join(',', #{ $data{$label} }), "\n"; #orig needs changing
print $format_file join(',', #{ $data{$label} }), "\n";
}
## END:: This part works on one file edited for this script ##
}
How do you plan on inputting the list of files to process and their preferred output destination? Maybe just have a fixed directory that you want to process all the cvs files, and prefix the result.
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
my $source_dir = '/some/dir/with/cvs/files';
my $output_prefix = 'format_';
opendir my $dh, $source_dir;
for my $file (readdir($dh)) {
next if $file !~ /\.csv$/;
next if $file =~ /^\Q$output_prefix\E/;
my $orig_file = "$source_dir/$file";
my $format_file = "$source_dir/$output_prefix$file";
.... old processing code here ...
}
Alternatively, you could just have an output directory instead of prefixing the files. Either way, this should get you on your way.