get hash ref key and value from another script - perl

Hi all
I have a module with a sub that get its parameters from e.g. script.pl
In script.pl I call the function this way moduleName::sunName(\%hashref).
Now in module, and in sub body I want to print those parameters that passed. also I want to check if the value of each key of this href is empty print '-' instead of 0.
first part of module:
sub printOptions {
my $opt = shift;
# I have this
print $opt->{'id'};
# But I need all parameters!
}
thanks

Try:
sub printOptions {
my $opt = shift #_;
for my $key ( sort keys %$opt ){
if( defined( $opt->{$key} )){
print "$key: $opt->{$key}\n";
}else{
print "$key: undef\n";
}
}
}

Matt, what are you getting at the moment? To dereference the reference $opt you can do
%opt = %{ $opt }
To iterate over the keys you can then do
for my $key ( sort keys %opt ) {
print "$key: " . ($opt{ $key } || '-') . "\n";
}

Related

Perl unexpected result

Imagine I have this Perl script
my $name = " foo ";
my $sn = " foosu";
trim($name, \$sn);
print "name: [$name]\n";
print "sn: [$sn]\n";
exit 0;
sub trim{
my $fref_trim = sub{
my ($ref_input) = #_;
${$ref_input} =~ s/^\s+// ;
${$ref_input} =~ s/\s+$// ;
};
foreach my $input (#_){
if (ref($input) eq "SCALAR"){
$fref_trim->($input);
} else {
$fref_trim->(\$input);
}
}
}
Result:
name: [foo]
sn: [foosu]
I would expect $name to be "[ foo ]" when printing the value after calling trim, but the sub is setting $name as I would want it. Why is this working, when it really shouldn't?
I'm not passing $name by reference and the trim sub is not returning anything. I'd expect the trim sub to create a copy of the $name value, process the copy, but then the original $name would still have the leading and trailing white spaces when printed in the main code.
I assume it is because of the alias with #_, but shouldn't the foreach my $input (#_) force the sub to copy the value and only treat the value not the alias?
I know I can simplify this sub and I used it only as an example.
Elements of #_ are aliases to the original variables. What you are observing is the difference between:
sub ltrim {
$_[0] =~ s/^\s+//;
return $_[0];
}
and
sub ltrim {
my ($s) = #_;
$s =~ s/^\s+//;
return $s;
}
Compare your code to:
#!/usr/bin/env perl
my $name = " foo ";
my $sn = " foosu";
trim($name, \$sn);
print "name: [$name]\n";
print "sn: [$sn]\n";
sub trim {
my #args = #_;
my $fref_trim = sub{
my ($ref_input) = #_;
${$ref_input} =~ s/^\s+//;
${$ref_input} =~ s/\s+\z//;
};
for my $input (#args) {
if (ref($input) eq "SCALAR") {
$fref_trim->($input);
}
else {
$fref_trim->(\$input);
}
}
}
Output:
$ ./zz.pl
name: [ foo ]
sn: [foosu]
Note also that the loop variable in for my $input ( #array ) does not create a new copy for each element of the array. See perldoc perlsyn:
The foreach loop iterates over a normal list value and sets the scalar variable VAR to be each element of the list in turn. ...
...
the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
In your case, this would mean that, at each iteration $input is an alias to the corresponding element of #_ which itself is an alias to the variable that was passed in as an argument to the subroutine.
Making a copy of #_ thus prevents the variables in the calling context from being modified. Of course, you could do something like:
sub trim {
my $fref_trim = sub{
my ($ref_input) = #_;
${$ref_input} =~ s/^\s+//;
${$ref_input} =~ s/\s+\z//;
};
for my $input (#_) {
my $input_copy = $input;
if (ref($input_copy) eq "SCALAR") {
$fref_trim->($input_copy);
}
else {
$fref_trim->(\$input_copy);
}
}
}
but I find making a wholesale copy of #_ once to be clearer and more efficient assuming you do not want to be selective.
I assume it is because of the alias with #_, but shouldn't the foreach my $input (#_) force the sub to copy the value and only treat the value not the alias?
You're right that #_ contains aliases. The part that's missing is that foreach also aliases the loop variable to the current list element. Quoting perldoc perlsyn:
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
So ultimately $input is an alias for $_[0], which is an alias for $name, which is why you see the changes appearing in $name.

perl variable not storing data outside block

I have written below mention code to read a file and and storing data to array #s_arr.
But when I am trying to print that #s_arr array outside the block it shows nothing.
use Data::Dumper;
my #s_arr;
my #err;
my %sort_h_1;
$fname = '/qv/Error.log';
open( IN, "<$fname" );
foreach $line ( <IN> ) {
if ( $line =~ /CODE\+(\w{3})(\d{5})/ ) {
$a = "$1$2";
push #err, $a;
}
}
close IN;
$prev = "";
$count = 0;
my %hash;
foreach ( sort #err ) {
if ( $prev ne $_ ) {
if ( $count ) {
$hash{$prev} = $count;
}
$prev = $_;
$count = 0;
}
$count++;
}
print Dumper \%hash;
printf( "%s:%d\n", $prev, $count ) if $count;
$hash{$prev} = $count;
my $c = 0;
print "Today Error Count\n";
foreach my $name ( sort { $hash{$b} <=> $hash{$a} } keys %hash ) {
#printf "%-8s %s\n", $name, $hash{$name};
#my %sort_h ;
push #s_arr, $name;
push #s_arr, $hash{$name};
#$sort_h{$name} = $hash{$name} ;
#print Dumper \%sort_h ;
#print Dumper \#s_arr ;
$c++;
if ( $c eq 30 ) {
exit;
}
}
print Dumper \#s_arr; # It's showing nothing
You are calling exit inside of your foreach loop. That makes the program stop, and the print Dumper #s_arr is never reached.
To break out of a loop you need to use last.
foreach my $name ( sort ... ) {
# ...
$c++;
last if $c == 30; # break out of the loop when $c reaches 30
}
I used the postfix variant of if here because that makes it way easier to read. Also note that as zdim pointed out above, you should use the numerical equality check == when checking for numbers. eq is for strings.

Printing out associated key with values in Perl in foreach loop

I am trying to print out the key for the hash if the value satisfies a certain condition. However, I am not sure how to access the hash key if it satisfies the value condition. This is the code I have:
foreach my $x (values %hash){
if ($x > $ARGV[1]){
$counter = $counter + 1
print "keys %hash\n"
}
}
print "$counter\n"
When you loop over the values, you have no access to the key.
for my $key (keys %hash) {
if ($hash{$key} > $ARGV[1]) {
$counter = $counter + 1;
print "$key\n";
}
}
print "$counter\n";
or
keys %hash; # reset iterator
while (my ($key, $value) = each %hash) {
if ($value > $ARGV[1]) {
$counter = $counter + 1;
print "$key\n";
}
}
print "$counter\n";
You can't access the key of a hash element given its value. After all, multiple keys may have the same value. But you can rely on Perl giving you the keys in the same order as the values. So you could write something like this
use strict;
use warnings;
my #keys = keys %hash;
my #vals = values %hash;
my $count = 0;
for my $val ( #values ) {
my $key = shift #keys;
if ( $val > $ARGV[1] ) {
++$count;
print $key, "\n";
}
}
print "$count\n";
But it would be far better to use a while loop with each to gather both the key and the value at the same time
while ( my ($key, $val) = each %hash ) {
if ( $val > $ARGV[1] ) {
++$count;
print $key, "\n";
}
}

Issue while comparing hashes of array in perl

I am pretty new to Perl and need to accomplish a task quickly. Any help is appreciated!
I have two hash of arrays as follows:
Hash 1
-------
abc.txt: ['0744','0']
xyz.txt: ['0744','0']
Hash 2
-------
abc.txt: ['0766','0']
x.txt: ['0744','0']
I have to compare these 2 hashes print 3 things:
1. Files Added in Hash2
2. Files Missing in Hash2
3. Files(keys) which are present in both hashes but there attributes(values) are different.
print "-------------------------ADDED FILES--------------------------------";
foreach (keys %hash2){
print "added $_\n" unless exists $hash1{$_};
}
print "-------------------------MISSING FILES--------------------------------";
foreach (keys %hash1){
print "Missing $_\n" unless exists $hash2{$_};
}
print "-------------------------Different permissions--------------------------------";
foreach my $key2 ( keys %hash2 ) {
unless ( exists $hash1{$key2} ) { next; };
if (join(",", sort #{ $hash1{$_}})
eq join(",", sort #{ $hash2{$_}}) ){
}
else{
print "value is different";
}
}
Issue is when keys are same.This for each loop doesn't work well.I want to print like this:
FileName: File Attributes Before : File Attributes after
abc.txt: '0744','0': 0766','0'
Please help
Your code didn't work, because you defined my $key2 in your foreach-loop, which leaves $_ as an empty value.
Also you don't need to join the hashes. Try the smartmatch operator on array values, its more efficient since you only need to do the join, when you want to have an output.
foreach my $key2 ( keys %hash2 ) {
unless ( exists $hash1{$key2} ) { next; };
unless ( $hash1{$key2} ~~ $hash2{ $key2 } )
{
print "$key2: ".join(",", #{ $hash1{$key2}}).": ".join(",", #{ $hash2{$key2}})."\n"
}
}
Change
foreach my $key2 ( keys %hash2 ) {
unless ( exists $hash1{$key2} ) { next; };
if (join(",", sort #{ $hash1{$_}})
eq join(",", sort #{ $hash2{$_}}) ){
}
else{
print "value is different";
}
}
to
foreach my $key2 ( keys %hash2 ) {
next unless ( exists $hash1{$key2} );
my $val1 = join(",", sort #{ $hash1{$key2} });
my $val2 = join(",", sort #{ $hash2{$key2} });
if ($val1 eq $val2) {
# values are same
}
else {
print "$key2 $val1 $val2\n";
}
}
and try again.

perl "eq" doesn't work well. I can't found my fault

I'm writting a small server with perl. There is some small problem. When the client give me a sentence like this "op:xxx:xxx:xxx", I'll get op. then do things depending on what op is. It works will if the op is adduser and so on. (I use if $op eq "adduser"...)
But when I get a "getList:xxx:xxx" and I have get the $op = getList, it can't pass it like "if $op eq "getList"". I know, it must be my mistake. But I just can't find it.
thank you for everyone.
use warnings;
use strict;
package MyPackage;
use base qw(Net::Server);
our %data_base;
our %tag_base;
sub list {
my %resault;
foreach ( keys %tag_base) {
print STDERR $_ . "1";
my #tags = split /:/, $tag_base{$_};
foreach ( #tags) {
$resault{$_} ++;
}
}
my #tags;
foreach ( keys %resault) {
push #tags, "$_,$resault{$_}";
}
$_ = join ";", #tags;
print ;
print STDERR ;
}
sub users {
my $topic = shift;
my #users;
foreach ( keys %tag_base) {
push #users, $_ if $tag_base{$_} =~ /$topic/;
}
$_ = join ";", #users;
print ;
}
sub process_request {
my $self = shift;
my $person;
my #info;
while (<STDIN>) {
my #gets = split /:/, $_;
print STDERR "#gets\n";
# $data_base{shift #person} = join ":", #person;
my $op = shift #gets;
$op =~ s/\s//;
print STDERR $op . "\n";
if ( $op eq "adduser") {
my $user_name = shift #gets;
if ( exists $data_base{$user_name}) {
print "already_exist";
} else {
$data_base{$user_name} = join ":", #gets;
print "addUserSu";
}
} elsif ( $op eq "login") {
my $login_name = shift #gets;
my $login_pw = shift #gets;
if ( defined $data_base{$login_name}) {
$person = $data_base{$login_name};
#info = split /:/, $person;
$info[0] =~ s/\s+//;
if ($login_pw eq $info[0]) {
print "$person";
} else {
print "/$info[0]/";
}
} else {
print "unexist_user";
}
} elsif ( $op eq "addTag") {
my $tag_user = shift #gets;
$tag_base{$tag_user} = join ":", #gets;
print "addTagSu";
} elsif ( $op eq "getList") {
print STDERR "right";
&list;
} elsif ( $op eq "getUsers") {
&users;
}
}
}
MyPackage->run(port => 13800);
I can see two (simple) reasons this might fail:
$op =~ s/\s//;
You only remove one whitespace: The first one. If your intention is to strip all whitespace, you'd want s/\s+//g.
And second:
Random capital letters in strings, variable names and commands is Evil. eq is case sensitive, so if $op is "getlist", then if ($op eq "getList") will be false. Unless capitalization is important to you, you could do if (lc($op) eq "getlist").
Without sample input, expected output and actual output, this is however nothing more than guesswork.
Also, as a debug statement, this is useless:
print STDERR $op . "\n";
That is easily confused and overlooked. For example, if $op is empty, it just produces a blank line in your error log. Use:
print STDERR "OP is: '$op'\n";
Now you will be able to identify the line where $op should appear, and you will be more easily see whitespace surrounding it.
You are reading strings without chomping them.
i.e.
When you run your code :
addtag:fred:barney
The input is stored as fred => "barney\n"
when you getList, the output is :
barney
,1;
I suspect the client is expecting 1 line of output that reads :
barney,1;
So, just add a chomp in your code here :
while (<STDIN>) {
chomp;
my #gets = split /:/, $_;