File comparison in Perl - by line or substring - perl

i m trying to compare 2 text files and i got down the following perl script, but for some reason even when i use the /same/ file as a base and filter, it doesnt output anything. I m really new to Perl, so apologies if any of this sounds base.
my $file_base = 'CSP8216.TXT';
my $file_filter = 'CSP8216.TXT';
open my $info_filter, $file_filter or die "Die: Could not open $file_filter: $!";
while(my $line_filter = <$info_filter>)
{
open my $info_base, $file_base or die "Die: Could not open $file_base: $!";
while(my $line_base = <$info_base>)
{
if("$line_filter"=="$line_base")
#if(substr($line_filter, 0, 11)==substr($line_base, 0, 11))
{
print $line_base;
}
}
close $info_bae;
}
close $info_filter;
Could someone point out why this doesnt seem to work?

Use eq to compare strings:
if($line_filter eq $line_base).
Also use use strict to see errors in your program

I would do it a little different
Obviously you probably done want to push the file into an array if they are large.
use strict;
use warnings;
use Data::Dumper;
my $file_base = '1.TXT';
my $file_filter = '2.TXT';
open ( FILTER, "<$file_filter" )
or die "Die: Could not open $file_filter: $!";
open ( BASE, "<$file_base" )
or die "Die: Could not open $file_base: $!";
my #filterArray = <FILTER>;
my #baseArray = <BASE>;
close BASE;
close FILTER;
unless( arrayDiff( \#filterArray , \#baseArray ) )
{
print "Success!";
}
sub arrayDiff {
my $array1 = shift(#_);
my $array2 = shift(#_);
my %array1_hash;
my %array2_hash;
# Create a hash entry for each element in #array1
for my $element ( #{$array1} ) {
$array1_hash{$element} = #{$array1};
}
# Same for #array2: This time, use map instead of a loop
map { $array2_hash{$_} = 1 } #{$array2};
for my $entry ( #{$array2} ) {
if ( not $array1_hash{$entry} ) {
return 1; #Entry in #array2 but not #array1: Differ
}
}
if ( keys %array1_hash != keys %array2_hash ) {
return 1; #Arrays differ
}
else {
return 0; #Arrays contain the same elements
}
}
perl 1156663.pl
Success!

Related

Perl: Get key value

I'm trying to get key values from hash inside my module:
Module.pm
...
my $logins_dump = "tmp/logins-output.txt";
system("cat /var/log/secure | grep -n -e 'Accepted password for' > $logins_dump");
open (my $fh, "<", $logins_dump) or die "Could not open file '$logins_dump': $!";
sub UserLogins {
my %user_logins;
while (my $array = <$fh>) {
if ($array =~ /Accepted\s+password\s+for\s+(\S+)/) {
$user_logins{$1}++;
}
}
return \%user_logins;
}
sub CheckUserLogins {
my $LoginCounter;
my $UsersToCheck = shift #_;
if (exists %{UserLogins()}{$UsersToCheck}){
$LoginCounter = %{UserLogins{$UsersToCheck}}; #How many logins?
}
else {
$LoginCounter = "0";
}
return \$LoginCounter;
}
Script.pl
$UserLoginCounter = Module::CheckUserLogins($UsersToPass);
I pass usernames to script and check if username is in hash, if it is, I need to return number of logins, which I'm trying to do with $LoginCounter. For some reason scripts returns only 0 or undef.
Well, for starters - you've got CheckUserLogins not CheckLoginAttempts.
Assuming that's just a typo - UserLogins returns a hash reference - a single scalar value. You're getting 0 if the exists check fails presumably.
If it does exist though, you're doing this:
$LoginCounter = %{UserLogins{$UsersToCheck}};
Which isn't valid. Do you have strict and warnings turned on? Because you're trying to assign a hash to a scalar, which isn't going to do what you want.
You probably mean:
$LoginCounter = ${UserLogins()} -> {$UsersToCheck};
Which dereferences the reference from UserLogins and then looks up a key.
I might however, approach your problem a little differently - it'll only work once when you do what you're doing, because each time you call UserLogins it creates a new hash, but you don't rewind $fh.
So I'd suggest:
use strict;
use warnings;
{
my %userlogins;
sub inituserlogins {
open( my $fh, "<", '/var/log/secure' )
or die "Could not open file: $!";
while ( my $array = <$fh> ) {
if ( $array =~ /Accepted\s+password\s+for\s+(\S+)/ ) {
$userlogins{$1}++;
}
}
close($fh);
}
sub CheckUserLogins {
my ($UsersToCheck) = #_;
inituserlogins() unless %userlogins;
return $userlogins{$UsersToCheck} ? $userlogins{$UsersToCheck} : 0;
}
}
You mustn't use capital letters in lexical identifiers as Perl reserves them for global identifiers like package names
One of the main problems is that you're using
exists %{UserLogins()}{$UsersToCheck}
which should be
exists UserLogins()->{$UsersToCheck}
or
exists ${UserLogins()}{$UsersToCheck}
Do you have use strict and use warnings in place as you should have?
Another problem is that you will read all the way through the file every time you call UserLogins. That means the second and later calls to CheckUserLogins (which calls UserLogins) will find nothing, as the end of the file has been reached
You should call your suibroutine user_logins and call it just once, storing the result as a scalar variable. This program shows how
use strict;
use warnings;
use v5.14; # For state variables
sub user_logins {
open my $fh, '<', '/var/log/secure' or die $!;
my %user_logins;
while ( <$fh> ) {
if ( /Accepted\s+password\s+for\s+(\S+)/ ) {
++$user_logins{$1};
}
}
\%user_logins;
}
sub check_user_logins {
my ($users_to_check) = #_;
state $user_logins = user_logins();
$user_logins->{$users_to_check} // 0;
}

How to distinguish between "0" and NULL in perl?

Here we are looking for the string "reftext" in the given file. The line next to this contains a string with 3 integers. So we are extracting them in #all_num. We are printing the value of #all_num[2] only if is not NULL. But the logic used here doesn't print #all_num[2] even if it has 0.
#!/usr/bin/perl
open( READFILE, "<myfile.txt" );
#list = <READFILE>;
$total_lines = scalar #list;
for ( $count = 0; $count < $total_lines; $count++ ) {
if (#list[ $count =~ /reftext/ )
{
#all_num = #list[ $count + 1 ] =~ /(\d+)/g;
if ( #all_num[2] != NULL ) {
print "#all_num[2]\n";
}
}
}
Hope this helps,
use strict;
use warnings;
my #fvals = (
[ i => undef ],
[ j => 0 ],
[ k => "" ],
);
for my $r (#fvals) {
my ($k, $v) = #$r;
if (!defined($v)) { print "$k is undef\n"; }
elsif (!length($v)) { print "$k is empty string\n"; }
# elsif (!$v) { print "$k is zero\n"; }
# recognizes zero value in "0.0" or "0E0" notation
elsif ($v == 0) { print "$k is zero\n"; }
}
output
i is undef
j is zero
k is empty string
Perl does not include a NULL, so the line
if(#all_num[2]!= NULL)
is nonsensical in Perl. (More accurately, it attempts to locate a sub named NULL and run it to get the value to compare against #all_num[2], but fails to do so because you (presumably) haven't defined such a sub.) Note that, if you had enabled use strict, this would cause a fatal error instead of pretending to work. This is one of the many reasons to always use strict.
Side note: When you pull a value out of an array, it's only a single value, so you should say $all_num[2] rather than #all_num[2] when referring to the third element of the array #all_num. (Yes, this is a little confusing to get used to. I hear that it's been changed in Perl 6, but I'm assuming you're using Perl 5 here.) Note that, if you had enabled use warnings, it would have told you that "Scalar value #all_num[2] better written as $all_num[2]". This is one of the many reasons to always use warnings.
If you want to test whether $all_num[2] contains a value, the proper way to express that in Perl is
if (defined $all_num[2])
This is how your program would look using best practices
You should
Always use strict and use warnings, and declare all your variables with my
Use the three-parameter form of open
Check that open calls succeeded, and include $! in the die string if not
Use a while loop to process a file one line at a time, in preference to reading the entire file into memory
#!/usr/bin/perl
use strict;
use warnings;
open my $fh, '<', 'myfile.txt' or die $!;
while ( <$fh> ) {
next unless /reftext/;
my $next_line = <$fh>;
my #all_num = $next_line =~ /\d+/g;
print "$all_num[2]\n" if defined $all_num[2];
}
Try this:
#!/usr/bin/perl
use warnings;
use strict;
open(READFILE, "<", "myfile.txt") or die $!;
my #list = <READFILE>;
my $total_lines = scalar #list;
close (READFILE);
for(my $count=0; $count<$total_lines; $count++)
{
if($list[$count] =~ /reftext/)
{
my #all_num = $list[$count+1] =~ /(\d+)/g;
if($all_num[2] ne '')
{
print "$all_num[2]\n";
}
}
}
To check a variable is null or not:
if ($str ne '')
{
print $str;
}
or better:
my ($str);
$str = "";
if (defined($str))
{
print "defined";
}
else
{
print "not defined";
}
If the other answers do not work, try treating the variable as a string:
if ( $all_num[2] == 'null' && length($all_num[2]) == 4 ){
# null
} else {
# not null
}
As with any code you write, be sure to test your code.

How to show matching and Miss matching records of two text files in command prompt using Perl?

I'm using two text files sampleA.txt and sampleB.txt. I have two fields in each file and I need to compare first record(first row) of sampleA.txt with the first row of sampleB.txt and I want to show matching records as well as miss matching records in command prompt.I need to do that in Perl.
Using the below script I'm getting one output but it is wrong. I need to populate both matching as well as mismatching. How to do that?
sampleA.txt:
1|X
2|A
4|Z
5|A
sampleB.txt:
2|A
2|X
3|B
4|C
Output I'm getting:
2|A
2|X
4|C
Outputs I want:
Matching-Output:
2|A
Miss-matching-Output:
1|X
4|Z
5|A
3|B
4|C
Perl Script:
#!/usr/bin/perl
use strict;
use warnings;
open(FILE1,'C:\Users\sathiya.kumar\Desktop\sampleA.txt') || die $!;
open(FILE2,'C:\Users\sathiya.kumar\Desktop\sampleB.txt') || die $!;
my $interline;
while (my $line= <FILE1>) {
my #fields = split('\|',$line);
parser($fields[0]);
}
sub parser {
my $mergeid = shift;
while (defined $interline || ($interline= <FILE2>)) {
my #fields = split('\|',$interline);
my $key = $fields[0];
if ($key lt $mergeid) {
# Skip non-matching records
$interline = undef;
next;
} elsif ($key gt $mergeid) {
# wait for next key
last;
} else {
print $interline;
$interline = undef;
}
}
}
close(FILE1);
close(FILE2);
Let me know if you need more information.
You left out 2|X:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
#Create a set from the entries in sampleA.txt:
my $fname = 'sampleA.txt';
open my $A_INFILE, '<', $fname
or die "Couldn't open $fname: $!";
my %a;
while (my $line = <$A_INFILE>) {
chomp $line;
$a{$line} = undef;
}
close $A_INFILE;
say Dumper(\%a);
#Create a set from the entries in sampleB.txt:
$fname = 'sampleB.txt';
open my $B_INFILE, '<', $fname
or die "Couldn't open $fname: $!";
my %b;
while (my $line = <$B_INFILE>) {
chomp $line;
$b{$line} = undef;
}
close $B_INFILE;
say Dumper(\%b);
#Divide the entries in both files into matches and mismatches:
my (#matches, #mismatches);
for my $a_val (keys %a) {
if (exists $b{$a_val}) {
push #matches, $a_val;
}
else {
push #mismatches, $a_val;
}
}
for my $b_val (keys %b) {
if (not exists $a{$b_val}) {
push #mismatches, $b_val;
}
}
say Dumper(\#matches);
say Dumper(\#mismatches);
--output:--
$VAR1 = {
'5|A' => undef,
'4|Z' => undef,
'1|X' => undef,
'2|A' => undef
};
$VAR1 = {
'2|X' => undef,
'3|B' => undef,
'4|C' => undef,
'2|A' => undef
};
$VAR1 = [
'2|A'
];
$VAR1 = [
'5|A',
'4|Z',
'1|X',
'2|X',
'3|B',
'4|C'
];
If you evaluate a hash in scalar context, it returns false if the hash is empty. If there are any key/value pairs, it returns true; more precisely, the value returned is a string consisting of the number of used buckets and the number of allocated buckets, separated by a slash. This is pretty much useful only to find out whether Perl's internal hashing algorithm is performing poorly on your data set. For example, you stick 10,000 things in a hash, but evaluating %HASH in scalar context reveals "1/16" , which means only one out of sixteen buckets has been touched, and presumably contains all 10,000 of your items. This isn't supposed to happen. If a tied hash is evaluated in scalar context, the SCALAR method is called (with a fallback to FIRSTKEY ).
http://perldoc.perl.org/perldata.html

mapping grep result to csv file

I'm trying to populate the grep result to csv file. But it is showing the following error.
"Use of uninitialized value in concatenation (.) or string at"
code:
sub gen_csv {
my $db_ptr = shift #_;
my $cvs_file_name = shift #_;
open( FILE, ">$cvs_file_name" ) or die("Unable to open CSV FILE $cvs_file_name\n");
print FILE "Channel no, Page no, \n";
foreach my $s ( #{$db_ptr} ) {
my $tmp = "$s->{'ch_no'},";
$tmp .= "$s->{'pg_no'},";
print FILE $tmp;
}
close(FILE);
}
sub parse_test_logs {
my $chnl;
my $page;
my $log = "sample.log";
open my $log_fh, "<", $log;
while ( my $line = <$log_fh> ) {
if ( $line =~ /(.*):.*solo_(.*): queueing.*/ ) {
my $chnl = $1;
my $page = $2;
}
my %test_details = (
'ch_no' => $chnl,
'pg_no' => $page, # <- was missing closing single quote
);
push( #{$dba_ptr}, \%test_details );
}
close log_fh;
}
Any suggestions on what i'm missing out?
(i'm getting the above error pointing to my $tmp = "$s->{'ch_no'},"; in gen_csv module)
Most likely this is due to NULL values in your DB records or the keys you are using are wrong. Either way, the warning is because the ch_no value does not exist.
If you don't care about NULL values, and you are fine with some of the values being missing, then you can suppress warnings for uninitialized values.
no warnings 'uninitialized';
Your problem involves this block:
if ( $line =~ /(.*):.*solo_(.*): queueing.*/ ) {
my $chnl = $1;
my $page = $2;
}
my %test_details = (
'ch_no' => $chnl,
'pg_no' => $page,
);
You're capturing your variables, but you have them declared with my within the if block. Those lexicals then go out of scope and are undef when used to initialize the hash.
I recommend simplifying your parsing function to the following:
sub parse_test_logs {
my $log = "sample.log";
open my $log_fh, "<", $log;
while (<$log_fh>) {
if ( my ( $chnl, $page ) = /(.*):.*solo_(.*): queueing.*/ ) {
push #{$dba_ptr}, { 'ch_no' => $chnl, 'pg_no' => $page };
} else {
warn "regex did not match for line $.: $_";
}
}
close $log_fh;
}
Finally, it's possible that you already are, but I just want to pass on the ever necessary advice to always include use strict; and use warnings; at the top of EVERY Perl script.

Compare and replace two files in perl

I am trying to compare the string in two documents test1, test 2
Test 1:
<p><imagedata rid="rId7"></p>
...
<p><imagedata rid="rId8"></p>
Test2:
<imagesource Id="rId7" Target="image/image1.jpg"/>
...
<imagesource Id="rId9" Target="image/image2.jpg"/>
...
<imagesource Id="rId8" Target="image/image3.jpg"/>
What I want is, the first file should get replaced with the image target path like:
<p><imagedata src="image/image1.jpg"></p>
...
<p><imagedata rid="image/image3.jpg"></p>
I tried to extract the text from both files but I stuck to compare both strings
opendir(DIR, $filenamenew1);
our(#test1,#test2);
open fhr, "$filenamenew1/test1.txt";
open fhr1, "$filenamenew1/test2.txt";
my #line;
#line= <fhr>;
for (my $i=0;$i<=$#line;$i++)
{
if ($line[$i]=~m/rid="(rId[0-9])"/)
{
my $k = $1;
push (#test1, "$k");
}
}
my #file2;
#file2= <fhr1>;
for (my $i=0;$i<=$#file2;$i++)
{
if ($file2[$i]=~m/Id="(rId[0-9])"/)
{
my $k1 = $1;
push (#test2, "$k1");
foreach (#test1 = #test2)
{
print "equal";
}
}
}
One solution could be to read first the file with <imagesources> and save both the rid and the target in a hash. After that read the other file line by line and compare if the rid exists in the hash and do the substitution, something like:
Content of script.pl:
#!/usr/bin/env perl
use warnings;
use strict;
my (%hash);
open my $fh2, '<', shift or die;
open my $fh1, '<', shift or die;
while ( <$fh2> ) {
chomp;
if ( m/Id="(rId\d+)".*Target="([^"]*)"/i ) {
$hash{ $1 } = $2;
}
}
while ( <$fh1> ) {
if ( m/rId="([^"]+)"/i && defined $hash{ $1 } ) {
s//src="$hash{ $1 }"/;
}
print $_;
}
Run it like:
perl script.pl test2 test1
That yields:
<p><imagedata src="image/image1.jpg"></p>
...
<p><imagedata src="image/image3.jpg"></p>