Creating Hash with keys only - perl

Can only keys be pushed in PERL hash, I mean, Can hash be created without corresponding values?
I want to create a hash which behave just like array i.e. hash with only keys but not corresponding values. Example is given below:
my %feedHash;
while(<CFG>)
{
chomp($_);
my #val=split(/:/,$_);
chomp($val[0]);
my $feedId=$val[0];
if(!exists $feedHash{$feedId})
{
print "\n$feedId Feed is not present";
$hash{$feedId} = undef;
mkdir "LoadReports/$feedId" or die $!;
}
else
{
print '\nFeed is already present';
}
}
It is giving message: "Feed is not present" even for same feed id second time in loop

You can make hash with your keys, where values are set to undef
my %hash;
#hash{qw(key1 key2 key3)} = ();
to check/set for particular hash key,
if (!exists $hash{key4}) { $hash{key4} = undef }

You're essentially wanting a %seen style hash.
You get no benefit from not actually giving the hash a true boolean value, and actually get additional headaches. I therefore suggest that you style your code like the following:
my %seen_feed;
while (<CFG>) {
chomp;
my ($id, #vals) = split ':';
if (! $seen_feed{$id}++) {
print "\n$id Feed is not present";
mkdir "LoadReports/$id" or die $!;
} else {
print '\nFeed is already present';
}
}

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

Store and get values from Perl hash tables

I want to store names in hash or array, which are in format
(e.g apple<->banana , orange<->papaya).
And now I have half information like apple or papaya which I need to look in that hash table and get the full combination apple<->banana and store it in a variable... :)
Hope my question is clear actually i read few hash documents and every where it's mentioned to search with full name ... so I need to search with half name or 1st word.
Assuming your input file is like:
apple<->banana
orange<->papaya
Here is a way to do the job:
#!/usr/bin/perl
use strict;
use warnings;
my %corresp;
while(<DATA>) {
chomp;
my ($k, $v) = split/<->/,$_;
$corresp{$k} = $v;
}
my %reverse = reverse %corresp;
my $search = 'apple';
if (exists$corresp{$search}) {
say "$search = $corresp{$search}";
} elsif(exists$reverse{$search}) {
say "$search = $reverse{$search}";
} else {
say 'No match!';
}
__DATA__
apple<->banana
orange<->papaya
Try this one:
my %hash = (
'apple' => 'banana',
'orange' => 'papaya'
);
## the word is looking for
my $word = 'orange';
## checking using Key
if(defined($hash{$word})){
print "$word <=> $hash{$word}";
}
## checking using value
else{
## handling only one value, not all
my ($key) = grep { $hash{$_} eq $word } keys %hash;
print "$key <=> $hash{$key}" if $key;
}

Push into end of hash in Perl

So what I am trying to do with the following code is push a string, let's say "this string" onto the end of each key in a hash. I'm completely stumped on how to do this. Here's my code:
use warnings;
use strict;
use File::Find;
my #name;
my $filename;
my $line;
my #severity = ();
my #files;
my #info = ();
my $key;
my %hoa;
my $xmlfile;
my $comment;
my #comments;
open( OUTPUT, "> $ARGV[0]" );
my $dir = 'c:/programs/TEST/Test';
while ( defined( $input = glob( $dir . "\\*.txt" ) ) ) {
open( INPUT, "< $input" );
while (<INPUT>) {
chomp;
if (/File/) {
my #line = split /:/;
$key = $line[1];
push #{ $hoa{$key} }, "Filename\n";
}
if ( /XML/ ... /File/ ) {
$xmlfile = $1;
push #{ $hoa{$key} }, "XML file is $xmlfile\n";
}
if (/Important/) {
push #{ $hoa{$key} }, "Severity is $_\n";
}
if (/^\D/) {
next if /Important/;
push #{ $hoa{$key} }, "Given comment is $_\n";
}
push #{ $hoa{$key} }, "this string\n";
}
}
foreach my $k ( keys %hoa ) {
my #list = #{ $hoa{$k} };
foreach my $l (#list) {
print OUTPUT $l, "\n";
}
}
}
close INPUT;
close OUTPUT;
Where I have "this string" is where I was trying to push that string onto the end of the array. However, what ended up happening was that it ended up printing "this string" three times, and not at the end of every key like I wanted. When I tried to put it outside the while() loop, it said that the value of $key was not initialized. So please, any help? And if you need any clarification on what I'm asking, just let me know. Thank you!
No offence, but there are so many issues in this code I don't even know where to start...
First, the 'initialization block' (all these my $something; my #somethings lines at the beginning of this script) is not required in Perl. In fact, it's not just 'redundant' - it's actually confusing: I had to move my focus back and forth every time I encountered a new variable just to check its type. Besides, even with all this $input var is still not declared as local; it's either missing in comments, or the code given has omissions.
Second, why do you declare your intention to use File::Find (good) - but then do not use it at all? It could greatly simplify all this while(glob) { while(<FH>) { ... } } routine.
Third, I'm not sure why you assign something to $key only when the line read is matched by /File/ - but then use its value as a key in all the other cases. Is this an attempt to read the file organized in sections? Then it can be done a bit more simple, either by slurp/splitting or localizing $/ variable...
Anyway, the point is that if the first line of the file scanned is not matched by /File/, the previous (i.e., from the previous file!) value is used - and I'm not quite sure that it's intended. And if the very first line of the first file is not /File/-matched, then an empty string is used as a key - again, it smells like a bug...
Could you please describe your task in more details? Give some test input/output results, perhaps... It'd be great to proceed in short tasks, organizing your code in process.
Your program is ill-conceived and breaks a lot of good practice rules. Rather than enumerate them all, here is an equivalent program with a better structure.
I wonder if you are aware that all of the if statements will be tested and possibly executed? Perhaps you need to make use of elsif?
Aside from the possibility that $key is undefined when it is used, you are also setting $xmlfile to $1 which will never be defined as there are no captures in any of your regular expressions.
It is impossible to tell from your code what you are trying to do, so we can help you only if you show us your output, input and say how to derive one from the other.
use strict;
use warnings;
use File::Find;
my ($outfile) = #ARGV;
my $dir = 'c:/programs/TEST/Test';
my %hoa;
my $key;
while (my $input = glob "$dir/*.txt") {
open my $in, '<', $input or die $!;
while (<$in>) {
chomp;
if (/File/) {
my $key = (split /:/)[1];
push #{ $hoa{$key} }, "Filename\n";
}
if (/XML/ ... /File/) {
my $xmlfile = $1;
push #{ $hoa{$key} }, "XML file is $xmlfile\n";
}
if (/Important/) {
push #{ $hoa{$key} }, "Severity is $_\n";
}
if (/^\D/) {
next if /Important/;
push #{ $hoa{$key} }, "Given comment is $_\n";
}
push #{ $hoa{$key} }, "this string\n";
}
close $in;
}
open my $out, '>', $outfile or die $!;
foreach my $k (keys %hoa) {
foreach my $l (#{ $hoa{$k} }) {
print $out $l, "\n";
}
}
close $out;
I suspect based on your code, that the line where $key is set is not called each time through the loop, and that you do not trigger any of the other if statements.
This would append "this string" to the end of the array. Based on that you are getting 3 of the "this strings" at the end of the array, I would suspect that two lines do not go through the if (/FILE/) or any of the other if statements. This would leave the $key value the same and at the end, you would append "this string" to the array, using whatever the last value of $key was when it was set.
This will append the string "this string" to every element of the hash %hoa, which elements are array refs:
for (values(%hoa)) { push #{$_}, "this string"; }
Put that outside your while loop, and you'll print "this string" at the end of each element of %hoa.
It will autovivify array refs where it finds undefined elements. It will also choke if it cannot dereference an element as an array, and will manipulate arrays by symbolic reference if it finds a simple scalar and is not running under strict:
my %autoviv = ( a => ['foo'], b => undef );
push #$_, "PUSH" for values %autoviv; # ( a => ['foo', 'PUSH'], b => ['PUSH'] )
my %fatal = ( a => {} );
push #$_, "PUSH" for values %fatal; # FATAL: "Not an ARRAY reference at..."
my %dangerous = (a => "foo");
push #$_, "PUSH" for values %dangerous; # Yikes! #foo is now ("PUSH")
use strict;
my %kablam = (a => "foo");
push #$_, "PUSH" for values %kablam; # "Can't use string ("foo") as an ARRAY ref ..."
As I understand it, traverse the hash with a map command to modify its keys. An example:
EDIT: I've edited because I realised that the map command can be assigned to the same hash. No need to create a new one.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash = qw|
key1 value1
key2 value2
key3 value3
|;
my %hash = map { $_ . "this string" => $hash{ $_ } } keys %hash;
print Dump \%hash;
Run it like:
perl script.pl
With following output:
$VAR1 = {
'key3this string' => 'value3',
'key2this string' => 'value2',
'key1this string' => 'value1'
};

Problems deferencing the Hash value from Net::Twitter

Im using the Net::Twitter module from CPAN, but I'm having a small issue with it.
The following subroutine searches for a term on Twitter but I cant seem to get anything
but a HASH value (ie. %HASH(0x9096dc0) )
How do I go about getting just the contents of the tweet?
sub twit_search
{
my $term = shift #_;
my $page = 1;
my #results;
while (scalar #results < $opts{maxresults})
{
my $rset = $handle->search({query=>$term, page => $page, rpp => $opts{rpp} });
print "Searching for $term (page $page)\n" if $opts{verbose};
if (ref $rset eq 'HASH' && exists $rset->{results})
{
last unless #{$rset->{results}};
push #results, #{$rset->{results}};
printf "Now we have %d entries\n", scalar #results if $opts{verbose};
}
$page++;
}
foreach my $tweet (#results)
{
print $tweet;
}
}
What you're getting back is actually a hash reference, which can be used to get at the hash containing the actual data. You can use Data::Dumper; print Dumper($tweet); to see the full structure and contents of the tweet.
It's been a year or so since I last wrote Twitter-related code, but I believe the actual text of the tweet should be in the "text" key, so, to print that, use
print $tweet->{text};