I've written a script that takes a command line argument -s, and allows me to add a search term after it.
It then feeds that into my first function, connects to a SQL database, searches for the term, and returns the results in an array.
It then calls the second function, prints the array, and outputs a -1 or a 0 depending on whether any results were found.
Finally it is supposed to check if the result is not equal to 0, and if so print out that no results were found.
Everything is working but my results are printing twice. Any idea why?
#!/usr/bin/perl -w
use warnings;
use DBI;
use Getopt::Std;
use strict;
getopt('s:');
our ($opt_s);
my $search = $opt_s;
my #array = function1($search);
&function1($search);
&function2(#array);
if (&function2(#array) != 0) {
print "No results found for '$search'", "\n";
}
sub function1 {
my $search = $_[0];
our $dbh = DBI->connect("dbi:mysql:dbname=database", "root", "password")
or die $DBI::errstr;
my $sql = $dbh->selectall_arrayref(
"SELECT Player from Players_Sport where Sport like '$search'")
or die $DBI::errstr;
my #array = map { $_->[0] } #$sql;
$dbh->disconnect
or warn "Disconnection failed: $DBI::errstr\n";
return #array;
}
sub function2 {
my #array = #_;
my $arrayvalue;
print("\n", "#array", "\n");
if (scalar(#array) == 0) {
$arrayvalue = -1;
}
else {
$arrayvalue = 0;
}
return $arrayvalue;
}
You're calling &function2(#array); twice, which causes "\n", "#array", "\n" to be printed twice. Just call the function once, store the return value in a variable, and test the variable rather than calling the function again — or, even better, in this specific instance you could just dispense with the first call entirely.
Related
I created this function.
When I print my variable my $bios_current, it shows $VAR1 = '200';
But my condition if ( $bios_current->responseCode() ne 200) considers that it is not 200.
Could you help me ? Is it a type problem ?
sub check_thermalshutdown_settings {
my $host = shift;
if ($host->get_property('summary.hardware.model') eq "ProLiant DL360 Gen9") {
my $error="";
my $bios="";
try {
my $ilo = get_ilo_address($host->name);
my $client = REST::Client->new();
$client->setHost("https://$ilo");
$client->addHeader("Authorization", "Basic blabla==");
eval {
local $SIG{ALRM} = sub { };
alarm 3;
#$client->GET("/redfish/v1/Systems/1/Bios/");
my $bios_current = $client->GET("/redfish/v1/Systems/1/Bios/");
print Dumper $bios_current->responseCode;
alarm 0;
};
if ( $bios_current->responseCode() ne 200) {
$bios = "none";
$error = "Redfish API returned code ".$client->responseCode();
print Dumper $client->responseCode();
} else {
my $json = decode_json($client->responseContent());
#print Dumper $client->responseContent();
#$bios = $json->{'Bios'}->{'Settings'}->{'ThermalShutdown'};
$bios = $json->{'ThermalShutdown'};
#print Dumper $bios;
print Dumper $json->{'ThermalShutdown'};
print "API call is ok\n";
print Dumper $client->setHost("https://$ilo");
}
} catch {
$bios = "none";
$error=$_;
};
You problem has nothing to do with type.
The first thing every Perl coder should learn is the following two statements should appear at the top of every script.
use strict;
use warnings;
These two statements catch a multitude of errors one of which is the cause of your problem.
If you take a look at your eval block
eval {
local $SIG{ALRM} = sub { };
alarm 3;
#$client->GET("/redfish/v1/Systems/1/Bios/");
my $bios_current = $client->GET("/redfish/v1/Systems/1/Bios/");
print Dumper $bios_current->responseCode;
alarm 0;
};
You will see that the variable $bios_current is introduced with the my modifier this restricts the lifetime of the variable to the current scope, in this case the eval block.
So by the time your if statement is run the variable no longer exists and Perl helpfully creates a new empty one for you, Perl then tries to call responseCode() on the empty variable, this fails and normally would terminate the program, however you are inside a try() block at this point so instead of displaying the error the code jumps to the catch bloc instead.
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;
}
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.
Is it possible in Perl to use a function's return value as the expression in an "if" statement? For example; in C I can write
if (!myFunction()){
printf("myFunction returned false.\n");
} else {
printf("myFunction returned true\n");
}
But in perl I find I must go through the pain of ..
$ret = myFunction();
if (!$ret){
print "myFunction returned false.\n";
}
I know as soon as I post this someone will redirect me to several other posts of this question. But, obviously, I could not find what I'm looking for or I would not write this!
So spare me the "have you tried searching for ...." messages!
Here is what myFunction() looks like.
sub myFunction
{
my ($run, $runTime) = #_;
my ($code);
eval {
$SIG{ALRM} = sub {die "Operation Timed Out";};
alarm($run_time);
$EXIT_STR = `$run`; # Execute $run and save output in EXIT_STR
$code = $?; # Save cmd exit code.
$EXIT_CODE = $code; # Set a global value (EXIT_CODE)
alarm(0);
return($code);
};
if ($#) {
if ($# =~ /Operation Timed Out/) {
print "Time out\n";
return(10);
}
}
}
After everyone's feedback I went back to the books to learn more about eval. After a bit of reading it was clearer that "eval" "returned" a value to the function it was part of. It was then up to me to decide what to do with the eval results. With that in mind I made some changes and the function works as I had hoped. Thanks to all!
Yup.
Wait. I can't give such a short answer...
Yes. If a function is inside an if statement, Perl will take the return value of the function as a boolean value. If the return value is zero, a blank string, a null value, a null string, or an undef, the if statement will be considered false. Otherwise, the if statement will be considered true.
Here's an easy to understand example:
if ( not is_odd( $number ) ) {
print "$number is divisible by two\n";
}
sub is_odd {
my $number = shift;
return $number % 2; # Modulo arithmetic
}
In the above $number % 2 will return zero on even numbers and one on odd numbers.
It's a good question. The best thing to do is to write a small script to try it out. Play around with it a bit:
For example. Let's add a second function we can use:
sub is_even {
my $number = shift;
return not is_odd( $number );
}
What does this return? How does this work in an if statement?
This will work fine.
The only caveat is that using it in an if-statement will provide scalar context to the return value so if something non-scalar is returned, it will get scalarized before the if condition is evaluated.
You may need to explicitly return a false value in cases where you don't want the function to return True. Remember that perl functions will return the last evaluated statement in the absence of a real return value. So, take this example:
#!/usr/bin/perl
use warnings;
use strict;
my $x = 4;
my $y = 1;
if ( ! myFunction($x,$y) ) {
print "myFunction returned false\n";
} else {
print "myFunction returned true\n";
}
sub myFunction {
my ($x,$y) = #_;
my $response;
if ( $x + $y == 2 ) {
$response = "fun";
} else {
$response = "no fun";
}
}
This will always print 'myFunction returned true' since either branch of the conditional is technically a true response. However, if you add a return value to the negative branch of the conditional, it will now work:
#!/usr/bin/perl
use warnings;
use strict;
my $x = 4;
my $y = 1;
if ( ! myFunction($x,$y) ) {
print "myFunction returned false\n";
} else {
print "myFunction returned true\n";
}
sub myFunction {
my ($x,$y) = #_;
my $response;
if ( $x + $y == 2 ) {
$response = "fun";
return 1; # technically not really needed
} else {
$response = "no fun";
return 0;
}
}
$ perl test_funct.pl
myFunction returned false
You're attempting to call return for your subroutine from inside an eval. This will not perform as you expect as explained in the docs for return:
return EXPR
return
Returns from a subroutine, eval, or do FILE with the value given in EXPR. ...
The bug you're facing can be demonstrated in the following example:
sub myFunction {
eval {
return "inside eval";
};
return "outside eval";
}
print myFunction();
Outputs:
outside eval
To fix your subroutine, assign your return value inside the eval, but actually return it from outside. Also, be sure to localize your alarm signal handler.
sub myFunction {
my ($run, $runTime) = #_;
my $code;
eval {
local $SIG{ALRM} = sub {die "Operation Timed Out";};
alarm($run_time);
$EXIT_STR = `$run`; # Execute $run and save output in EXIT_STR
alarm(0);
$code = $?; # Save cmd exit code.
};
if ($#) {
if ($# =~ /Operation Timed Out/) {
print "Time out\n";
$code = 10;
}
}
return $code;
}
I want to add the return value from the function call to an array iff something is returned (not by default, i.e. if I have a return statement in the subroutine.)
so I'm using unshift #{$errors}, "HashValidator::$vfunction($hashref)"; but this actually adds the string of the function call to the array. I also tried unshift #{$errors}, $temp if defined my $temp = "HashValidator::$vfunction($hashref)"; with the same result. What would a perl one-liner look like that does this efficiently (I know I can do the ugly, multi-line check but I want to learn).
Thanks,
iff something is returned (not by
default, i.e. if I have a return
statement in the subroutine.)
There could be a gotcha here. Perl always returns something, even if you don't mean to:
my $failures = 0;
sub word_to_number {
my $_ = shift;
/one/ and return 1;
/two/ and return 2;
++$failures; # whoops, equivalent to return ++$failures
}
The last expression in a sub is used as the return value if there is no explicit return. To return "nothing", use bare return, which returns undef or the empty list, depending on context:
my $failures = 0;
sub word_to_number {
my $_ = shift;
/one/ and return 1;
/two/ and return 2;
++$failures;
return;
}
This behaviour is actually useful for things like sorting:
my #results = sort { $a->name cmp $b->name } #list;
where we have passed in the anonymous subroutine:
{
$a->name cmp $b->name # equivalent to return $a->name cmp $b->name
}
In this case, there is no need to use the string form of eval (or any form for that matter). Not only is it slow, but it also can silently trap errors and could lead to untrusted code execution if used with a tainted input. To write a virtual function call in Perl, you can either work with the symbol table directly, or use a symbolic reference:
use 5.010;
use warnings;
use strict;
{package HashValidator;
sub test_ok {exists $_[0]{ok}}
sub test_fail {exists $_[0]{fail}}
}
my $hashref = {ok => 1};
my $errors;
for my $vFunction qw(test_ok test_fail) {
# to call the function:
say "glob deref: $vFunction: ", $HashValidator::{$vFunction}->($hashref);
{no strict 'refs';
say "symbolic: $vFunction: ", &{"HashValidator::$vFunction"}($hashref)}
# to conditionally use the result (if it is a true boolean value):
if (my $ret = $HashValidator::{$vFunction}->($hashref)) {
push #$errors, $ret;
}
# or to keep the function call in list context:
push #$errors, grep $_, $HashValidator::{$vFunction}->($hashref);
# or to golf it:
push #$errors, $HashValidator::{$vFunction}->($hashref) || ();
}
say #$errors.': ', join ', ' => #$errors;
which prints:
glob deref: test_ok: 1
symbolic: test_ok: 1
glob deref: test_fail:
symbolic: test_fail:
3: 1, 1, 1
If you are working with object oriented code, virtual method calls are even easier, with no symbol table or symbolic references:
$obj->$vMethod(...)
try using eval:
push #{$errors}, eval "HashValidator::$vfunction($hashref)"
The following works for me with perl 5.12, and checks for undef return values:
my $foo = "foo";
my $val = eval "Foo::$foo()"
push #arry,$val if ($val);