passing Array by reference perl - arrays

The problem with code is petlist2_out has last lines of petlist1_out when inputfile1 has more lines than inputfile2. it looks like array passed by reference is to function and copied back is keeping older array content. Please help. the code is working perfectly for all other cases. i am not sure on how to clean the arrays and varaibles other than using undef. When using references how to avoid using same location when function is called multiple times?
i am not a professional in Perl coding.
use File::Basename;
#use warnings;
if ($#ARGV != 1 )
{
print "usage: sort_petlist_extract.pl old-petlist new-petlist";
exit;
}
print "\n****** Reading petlist .....\n";
$inputfile1 = shift(#ARGV); #get text based IN filename
$inputfile2 = shift(#ARGV); #get text based IN filename
my $result = dirname $inputfile2;
print $result;
#my $filename = basename $filespec;
#$result = 'C:\\temp\\';
#my $inputfile=$ARGV[0];
open(log_file, "> ".$result.'\\'."debug.log");
open(diff_rpt_file, "> ".$result.'\\'."diff_report.txt");
print log_file $inputfile1."\n";
print log_file $inputfile2."\n";
open(petlist1, "< $inputfile1") or die "\nCouldn't open input file\n";
my #input_lines1 = <petlist1>;
close petlist1;
chomp #input_lines1;
#foreach(#input_lines1){print log_file $_."\n";}
my($petlist1_2d_ref)=extract_petlist_file(\#input_lines1);
my %petArray1 = %{$petlist1_2d_ref};
undef #input_lines1;
print log_file $petArray1_ref ."\n";
print scalar keys %petArray1;
open(petlist1_out, "> ".$result.'\\'."petlist1_out.txt");
#just test a display
for($i=0;$i<(scalar keys %petArray1);$i++)#just a display
{
print petlist1_out "${$petArray1{$i}}[0] \t ${$petArray1{$i}}[1] \n";
}
close petlist1_out;
undef %petArray1;
print log_file "###############################################################################first file copied to array#######################################\n";
print "####################first file copied to array########################\n";
open(petlist2, "< $inputfile2") or die "\nCouldn't open input file\n";
my #input_lines2 = <petlist2>;
close petlist2;
chomp #input_lines2;
#foreach(#input_lines2){print log_file $_."\n";}
undef $petlist2_2d_ref;
my($petlist2_2d_ref)=extract_petlist_file(\#input_lines2);
my %petArray2 = %{$petlist2_2d_ref};
#my($page_petname)=extract_page_petname(\#input_lines2);### page number extracttion
undef #input_lines2;
print log_file $petArray2_ref."\n";
print scalar keys %petArray2;
open(petlist2_out, "> ".$result.'\\'."petlist2_out.txt");
#just test a display
for($i=0;$i<(scalar keys %petArray2);$i++)#just a display
{
print petlist2_out "${$petArray2{$i}}[0] \t ${$petArray2{$i}}[1] \n";
}
close petlist2_out;
print log_file "###############################################################################second file copied to array #######################################\n";
print "####################second file copied to array ######################\n";
####################### sub routines
sub extract_petlist_file
{
undef $arr1;
my $arr1=$_[0];
undef #petlist;
my #petlist=#$arr1;
#foreach(#petlist){print log_file $_."\n";}
my $temp1='';
my $temp2='';
undef #temp;#clear 1D array
my #temp;
undef $_petArray;
my $_petArray;
undef $i;
print log_file scalar #petlist."\n";
my $k=0;
my $pattern1='PET_NAME';
for(my $i=0;$i<(scalar #petlist);$i++)
{
#print "$i\n";
#print log_file $_;
$_=$petlist[$i];
if ((m/$pattern1/) && $combine_flag eq 0)# first time ever in the loop
{
}
}
#store the last pet connection to array
#temp=($temp1,$temp2);
$_petArray{$k}=[#temp];
undef #temp;
undef $temp1;
undef $temp2;
return \%_petArray;
}

Thanks for the guidance. The strict and warnings helped. The actual issue was "my $_petArray;" had to be "my %_petArray;". I was using it as a variable instead of declaring as array.

Related

Perl: How to reference to an array?

I have a program here that will allow user to enter in words that will be used as filenames later on. The user's input is inserted into an array. I then create a simple menu asking what the user wants to do with the "filename": create, reame, copy, and delete. Using subroutines, I believe I've successfully executed the code. However, I am having trouble trying to reference the array element.
In short, how do I create, rename, copy, and delete the filenames that are saved in the array? The issues I cant resolve are asterisked
#!/usr/bin/perl
use strict;
use warnings;
use File::Copy qw/copy/;
my $new;
my $old;
my $file;
print "Enter number of lines\n";
chomp(my $n = <STDIN>);
my #lines;
print "Enter some words!\n";
for (1..$n) {
chomp(my $input = <STDIN>);
#will add the user input at the beginning of the array
push #lines, $input;
}
print "#lines\n";
#create a menu
my $in = '';
print "1. Create\n2. Rename\n3. Copy\n4. Delete\n5. Quit\n";
while ($in ne "quit")
{
print "\nEnter the your choice:\n";
chomp($in = <STDIN>);
print "For which file do you want to $in?\n";
print "#lines\n";
**#I need to reference the array here***********************
if ($in eq "create") {
***How to create the file that the user inputted?*****************
&createFile;
print "\nFile has been created\n";
}
elsif ($in eq "rename") {
#print "Enter the old name\n";
# chomp($old = <STDIN>);
#*********refer to array of FILES*********************
print "Enter the new name for this file!\n";
chomp($new = <STDIN>);
&renameFile;
print "File has successfully been renamed.";
}
elsif ($in eq "copy") {
#********file and array refering***************************
&copyFile;
print "Hopefully, the file has beed copied!";
}
elsif ($in eq "delete") {
#******refer to file from user*******************
&deleteFile;
print "Deleted";
}
}
#create subroutine
sub createFile {
open('>' .*****issue here too**) or die "\nCannot create";
}
sub renameFile {
rename ($old, $new);
}
sub copyFile {
copy $old, $new;
}
sub deleteFile {
#unlink *****************************
}
First line is $line[0], second is $line[1] and so on...
You can get user choice like this:
$line[$in]
About how to refer to perl data structures see here
$lines[0]; #First line
$lines[1]; #Second line
$array = \#lines; # Get reference to the list of lines
$array->[0]; #First line
$array->[1]; #Second line
to create file and put data into just use:
open $fh, '>filename';
print $fh #lines;
print $fh #$array; # ... or same for reference
Notice lack of coma after $fh

printing arrays to text file (v2)

yesterday i asked this question and got many helpful replies, hoping the same will be true today. Here is my revised script.
#! /usr/bin/perl
use strict;
use warnings;
my $line;
my #array;
my $print;
open (OUT , ">","output.txt")or die "cant open: $!";
while ($line = <>){
chomp($line);
push(#array, $line);
if(#array == 250){
$print = print "[", join(",",#array), "]", "\n";
print OUT $print;
#array = []
}
}
Originally i simply stated that i need to print out the first 250 elements of an array to a text file, and that this array was built from standard input. What i didnt state is that the input, from which the array is built, may consist of several thousand lines. The reason for creating an array from this input is so that i can limit the size of the array to 250 entities, and then print the array as a formatted string. I then need to flush the array and resume building at what will become the 251st line of the input, and continue doing this process for the remainder of the input. Lets say the input is 5k lines, i want my output to be a text file containing the original 5k lines of input, but divided into strings made up of 250 array entities.
Currently the script is just printing the array to the screen and inside "ouput.txt" is a single line reading: 1111111.
$print = print "[", join(",",#array), "]", "\n";
print OUT $print;
Here you are assigning the return value of print to $print, which is 1, because the printing is successful. What you want to do is this:
print OUT "[", join(",",#array), "]", "\n";
Here's another option which just slightly modifies your script:
use strict;
use warnings;
my #array;
local $" = ',';
while (<>) {
chomp;
push #array, $_;
if ( #array == 250 ) {
print "[#array]\n";
undef #array;
}
}
Usage: perl script.pl Infile [>outFile]
The last, optional parameter directs output to a file.
The variable $" is holds Perl's list separator that's applied to an interpolated array, so join's not needed here.
Hope this helps!
#! /usr/bin/perl
use strict;
use warnings;
my $line;
my #array;
my $print;
open (OUT , ">","moloch_chunker_output.txt")or die "cant open: $!";
while ($line = <>){
chomp($line);
push(#array, $line);
if(#array == 250){
print OUT "[", join(",",#array), "]", "\n";
#array = ();
}
}
Thank you davs, that fixed it.

Printing an array variable displaying all its elements using main:: .

The #main::match_to_array prints out only the last element in the array #match_to_array , not the whole array.
I did my code with reference to this SO link.
The input HTML consists of
dmit#sp.com
ems#es.com
dew#es.com
dmit#sp.com
erg#es.com
#!/usr/bin/perl –w
use strict;
use warnings;
use Cwd;
sub extractMail {
my $perl_path = cwd;
# Full HTML.htm
if(-e 'test.html') {
open(OPENFILE, "$perl_path/test.html") or die "Unable to open file";
}
my #email = <OPENFILE>;
close OPENFILE;
foreach my $email (#email){
if ($email =~ /regex to match data/{
my $match = "$1\n";
our #match_to_array = split ("\n",$match);
} # end of if statement
} # end of foreach
} # end of subroutine extractMail
for (my $a = 1;$a<=1;$a++){
&extractMail;
print #main::match_to_array;
}
You have misunderstood the post. The point is to declare the variable at the right place. In this case, you should probably return the value from the subroutine. Moreover, by assigning to an array
#match_to_array = split /\n/, $match;
you are overwriting the previous contents of the array. Use push instead.
Untested:
#!/usr/bin/perl –w
use strict;
use warnings;
use Cwd;
sub extractMail {
my $perl_path = cwd;
if (-e 'test.html') {
open my $OPENFILE, "$perl_path/test.html" or die "Unable to open file: $!";
}
my #match_to_array;
while (my $email = <$OPENFILE>) {
if ($email =~ /regex to match data/) {
my $match = "$1\n";
push #match_to_array, split /\n/, $match;
}
}
return #match_to_array;
}
for my $i (1 .. 1) {
my #match_to_array = extractMail();
print "#match_to_array\n";
}
my #email = <OPENFILE>;
close OPENFILE;
This might be the Problem, after those lines #email is containg one element, namely "dmit#sp.com ems#es.com ...".
Afterwards you're doing this:
foreach my $email (#email)
This will loop once, with
$email = "dmit#sp.com ems#es.com ..."
Then your regex removes everything BUT "dmit#sp.com" and leads you to the conclusion that only one element of your list is processed.
Try reading up on split to generate an array out of your space-separated list

read multiple files using one loop perl

i have 2 files each having 50 lines..
FILE1
FILE2
now, i need to read two file lines by line in a single while or for loop and i should push the corresponding line to the 2 output arrays. i have tried something like this. but its not working out. kindly help
#!/usr/bin/perl
my #B =();
my #C =();
my #D =();
my $lines = 0;
my $i = 0;
my $sizeL = 0;
my $sizeR = 0;
my $gf = 0;
$inputFile = $ARGV[0];
$outputFile = $ARGV[1];
open(IN1FILE,"<$inputFile") or die "cant open output file ";
open(IN2FILE,"<$outputFile") or die "cant open output file";
while((#B=<IN1FILE>)&&(#C= <IN2FILE>))
{
my $line1 = <IN1FILE>;
my $line2 = <IN2FILE>;
print $line2;
}
Here array 2 is not getting build.. but i am getting array 1 value.
In your loop condition, you read the whole files into their arrays. The list assignment is then used as a boolean value. This works only once, as the files will be read after the condition has been evaluated. Also, the readlines inside the loop will return undef.
Here is code that should work:
my (#lines_1, #lines_2);
# read until one file hits EOF
while (!eof $INFILE_1 and !eof $INFILE_2) {
my $line1 = <$INFILE_1>;
my $line2 = <$INFILE_2>;
say "from the 1st file: $line1";
say "from the 2nd file: $line2";
push #lines_1, $line1;
push #lines_2, $line2;
}
You could also do:
my (#lines_1, #lines_2);
# read while both files return strings
while (defined(my $line1 = <$INFILE_1>) and defined(my $line2 = <$INFILE_2>)) {
say "from the 1st file: $line1";
say "from the 2nd file: $line2";
push #lines_1, $line1;
push #lines_2, $line2;
}
Or:
# read once into arrays
my #lines_1 = <$INFILE_1>;
my #lines_2 = <$INFILE_2>;
my $min_size = $#lines_1 < $#lines_2 ? $#lines_1 : $#lines_2; # $#foo is last index of #foo
# then interate over data
for my $i ( 0 .. $min_size) {
my ($line1, $line2) = ($lines_1[$i], $lines_2[$i]);
say "from the 1st file: $line1";
say "from the 2nd file: $line2";
}
Of course, I am assuming that you did use strict; use warnings; and use feature 'say', and used the 3-arg form of open with lexical filehandles:
my ($file_1, $file_2) = #ARGV;
open my $INFILE_1, '<', $file_1 or die "Can't open $file_1: $!"; # also, provide the actual error!
open my $INFILE_2, '<', $file_2 or die "Can't open $file_2: $!";
I also urge you to use descriptive variable names instead of single letters, and to declare your variables in the innermost possible scope — declaring vars at the beginning is almost the same as using bad, bad globals.

Checking for Duplicates in array

What's going on:
I've ssh'd onto my localhost, ls the desktop and taken those items and put them into an array.
I hardcoded a short list of items and I am comparing them with a hash to see if anything is missing from the host (See if something from a is NOT in b, and let me know).
So after figuring that out, when I print out the "missing files" I get a bunch of duplicates (see below), not sure if that has to do with how the files are being checked in the loop, but I figured the best thing to do would be to just sort out the data and eliminate dupes.
When I do that, and print out the fixed data, only one file is printing, two are missing.
Any idea why?
#!/usr/bin/perl
my $hostname = $ARGV[0];
my #hostFiles = ("filecheck.pl", "hostscript.pl", "awesomeness.txt");
my #output =`ssh $hostname "cd Desktop; ls -a"`;
my %comparison;
for my $file (#hostFiles) {
$comparison{$file} +=1;
}
for my $file (#output) {
$comparison{$file} +=2
}
for my $file (sort keys %comparison) {
#missing = "$file\n" if $comparison{$file} ==1;
#print "Extra file: $file\n" if $comparison{$file} ==2;
print #missing;
}
my #checkedMissingFiles;
foreach my $var ( #missing ){
if ( ! grep( /$var/, #checkedMissingFiles) ){
push( #checkedMissingFiles, $var );
}
}
print "\n\nThe missing Files without dups:\n #checkedMissingFiles\n";
Password:
awesomeness.txt ##This is what is printing after comparing the two arrays
awesomeness.txt
filecheck.pl
filecheck.pl
filecheck.pl
hostscript.pl
hostscript.pl
The missing Files without dups: ##what prints after weeding out duplicates
hostscript.pl
The perl way of doing this would be:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my %hostFiles = qw( filecheck.pl 1 hostscript.pl 1 awesomeness.txt 1);
# ssh + backticks + ls, not the greatest way to do this, but that's another Q
my #files =`ssh $ARGV[0] "ls -a ~/Desktop"`;
# get rid of the newlines
chomp #files;
#grep returns the matching element of #files
my %existing = map { $_ => 1} grep {exists($hostFiles{$_})} #files;
print Dumper([grep { !exists($existing{$_})} keys %hostFiles]);
Data::Dumper is a utility module, I use it for debugging or demonstrative purposes.
If you want print the list you can do something like this:
{
use English;
local $OFS = "\n";
local $ORS = "\n";
print grep { !exists($existing{$_})} keys %hostFiles;
}
$ORS is the output record separator (it's printed after any print) and $OFS is the output field separator which is printed between the print arguments. See perlvar. You can get away with not using "English", but the variable names will look uglier. The block and the local are so you don't have to save and restore the values of the special variables.
If you want to write to a file the result something like this would do:
{
use English;
local $OFS = "\n";
local $ORS = "\n";
open F, ">host_$ARGV[0].log";
print F grep { !exists($existing{$_})} keys %hostFiles;
close F;
}
Of course, you can also do it the "classical" way, loop trough the array and print each element:
open F, ">host_$ARGV[0].log";
for my $missing_file (grep { !exists($existing{$_})} keys %hostFiles) {
use English;
local $ORS = "\n";
print F "File is missing: $missing_file"
}
close F;
This allows you to do more things with the file name, for example, you can SCP it over to the host.
It seems to me that looping over the 'required' list makes more sense - looping over the list of existing files isn't necessary unless you're looking for files that exist but aren't needed.
#!/usr/bin/perl
use strict;
use warnings;
my #hostFiles = ("filecheck.pl", "hostscript.pl", "awesomeness.txt");
my #output =`ssh $ARGV[0] "cd Desktop; ls -a"`;
chomp #output;
my #missingFiles;
foreach (#hostFiles) {
push( #missingFiles, $_ ) unless $_ ~~ #output;
}
print join("\n", "Missing files: ", #missingFiles);
#missing = "$file\n" assigns the array #missing to contain a single element, "$file\n". It does this every loop, leaving it with the last missing file.
What you want is push(#missing, "$file\n").

Resources