Why does this statment return true - perl

Why does this statement,
if (! $ssh_options{user}) {
delete $ssh_options{user};
}
return true, but this statement
if ($ssh_options{user} eq 'undef') {
delete $ssh_options{user};
}
give me the error,
Use of uninitialized value $ssh_options{"user"} in string eq at analyze.pl line 230.
I thought you always had to have something for perl to compare the value of the variable being compared.
** Update **
# Quick Joe Smith
I cannot base my comparison on whether the hash keys exist or not because the values for this hash
sub ssh_connect {
my $host = shift;
my %ssh_options = (
port => shift,
user => shift,
password => shift
);
Come from this function which calls the sub routine
if ((exists $config_file{user}) && (exists $config_file{password})) {
my $vmware_user = $config_file{user};
my $vmware_password = $config_file{password};
ssh_connect($vmware_host, $vmware_port, $vmware_user, $vmware_password);
} else {
ssh_connect($vmware_host, $vmware_port);
}

Perl hash slots have a number of ways they can be false. If the key has never been filled, the exists function will return false. If the key exists but contains an undefined value, exists will be true but the defined function will return false. In normal boolean context, as given by an if statement, a hash key that was never filled, or is undefined, '', 0 or anything that reduces to those will be false.
From your update, it sounds like what you want is to use defined
delete $ssh_options{user} unless defined $ssh_options{user};

Use exists for checking hash keys.
if (exists $ssh_options{user}) {
...
}
However, in your case, it seems as though you're checking for existing keys whose values may be undefined, in which case:
if (defined $ssh_options{user}) {
...
}
The above may be what you want.
As a sidenote, checking to see if the value of any variable is defined in the way you described:
if ($something eq 'undef') {
...
}
Is wrong. You're checking to see if the variable contains the string "undef". What you need in those situations is:
unless (defined $something) {
...
}

But if $ssh_options{"user"} is uninitialized then it it doesn't have a value to compare to.

With Perl, undefined evaluates to false in Boolean context. So your first test 'works' as you expect. However, it might introduce a bug if zero or an empty string is a legitimate value for $ssh_options{user} to hold, if you don't wish to treat that the same as undef. That's because your first test doesn't differentiate between false as a value, and false as undefined. As far as Perl's Boolean evaluation goes, all of the following are "false": 0, '' (empty string), undef, or an empty list.
Your second snippet of code fails because undef should be a bareword, not a quoted string. 'undef' as a quoted string IS a value, which would actually evaluate to 'true' in Boolean tests. Because you're comparing an actual value to your hash element, Perl warns you that you're comparing something to an undefined value. In this case, the undefined value is $ssh_options{user}. It's actually good that Perl is warning you; it's given you a clue as to what you're doing wrong.
If you really want to test whether $ssh_options{user} is defined, use the defined() function. If you want to test whether $ssh_options{user} exists, use the exists() function.

Related

How to check whether hash has a value for the key in puppet

I have a hash defined as below:
Hash[String, String] $hashtest = { "abc" => "test1", "xyz" => "test2" },
I have String variable, I need to search for the given key in the hash and if a value is found, I need to assign that value to the variable "result" otherwise I need to assign a default value "test". How can I do this is in puppet? Or only way to do this is using if else condition?
It should be similar like this, but the below code is not working. Kindly correct me what I'm doing wrong.
String $variable = $hashtest[$key] ? { true => $hashtest[$key], false => "test" },
It would be really helpful if someone helps me with this thanks in advance.
I am assuming in your pseudocode you are intending to assign a value with a return from a selector, and not also providing a pseudocode for a ternary-like expression in Puppet. With that in mind, we can achieve this with something similar to Python:
String $variable = $key in $hashtest ? {
true => $hashtest[$key]
false => "test"
}
Note that prior to Puppet 4 you would need the has_key? function (analogous to has_key Hash method in Ruby) from stdlib:
String $variable = has_key($hashtest, $key) ? {
true => $hashtest[$key]
false => 'test'
}
In stdlib there is also a function roughly equivalent to a "null coalescing" operator in other languages (null being roughly equivalent to undef type in Puppet and nil in Ruby) that would provide a cleaner expression:
String $variable = pick($hashtest[$key], 'test')
Similar to the coalescing patterns in other languages, pick will return the first argument that is not undef or empty.
As well as matts answer you can also use the following
$variable = $hashtest[$key].lest || { 'test' }
$variable = ($key in $hashtest).bool2str($hashtest[$key], 'test')
$variable = $hashtest.has_key($key).bool2str($hashtest[$key], 'test')
All of these options are missing the most simple and powerful option that's been available from the core library since puppet 6; the get function.
The get function allows you specify a dot separated path of nested keys to look up as the first argument, and a default value for the second. By default, it will return undef if a value cannot be found, making it ideal for use in conditional expressions since undef is the only value in puppet that automatically converts to false. You can even pass a lambda to it to handle missing values.
In your case, the answer is as simple as $variable = $hashtest.get($key, 'test') or $variable = get($hashtest, $key, 'test'), though I personally find the first option easier to read.

Perl error, cant use defined(#array). How can i fix this?

I am getting this error -> "Can't use 'defined(#array)' (Maybe you should just omit the defined()?)"
On this line of code:
if ( defined( #{ $linkbot{"$nroboton"} } ) ) {
How can i fix this?
defined tests whether a scalar value is undef, therefore it's nonsensical on an array. You can test whether the scalar is defined before using it as an arrayref, or if you're trying to test if the array is empty, just removed the defined() as the error message says.
# if this hash value is defined
if (defined $linkbot{$nroboton}) {
# if this referenced array has elements
if (#{ $linkbot{$nroboton} }) {
Use define on the variable $nroboton itself, and/or if (#{$linkbot{$nroboton}}) for the anonymous array whose reference is the value for that key, as explained.
Once you need to check on any of that it stands to reason that you may well also need to test whether there is a key $nroboton in the hash %linkbot, with exists
if ( exists $linkbot{$nroboton} ) { ... } # warning if $nroboton undef
so altogether
if (defined $nroboton and exists $linkbot{$nroboton}) { ... }
and now you can check and work with the data in the arrayref, #{$linkbot{$nroboton}}.
Note that there is no need to double-quote that variable; it will be evaluated.

Evaluating the success/failure of a subroutine

There's something quite unclear to me about subs return value.
I like to test my modules, sub by sub, and check whether they issue the correct return value or the correct exception if the case arise.
For example, let's say I have the following code (X::Argument::BadFormat is an exception handler derived from Exception::Class):
package My::Module;
use strict;
use warnings;
sub new{#does things unrelated to the current question}
sub my_sub {
my ($self,$possible_value) = #_;
if ($possible_value =~ q{\w}) { #Affect value to current object
$self->{field} = $possible_value;
}else{ #throw an exception
X::Argument::BadFormat->throw(
arg => 'possible_value',
expected_format => 'something that looks like a word',
received_value => $possible_value,
);
}
}
In the test file, I will run tests such as:
my $object = My::Module->new();
throws_ok(sub {$object->my_sub('*')}, 'X::Argument::BadFormat', 'Faulty value will raise an exception');
ok($object->my_sub('turlututu'));
It is easy to test when:
the sub returns a value,
the test conditions must raise an exception,
However, when I just set the value of a field in the current object, I have no reason to return anything.
In that case:
is the simple execution of the code enough to evaluate the sub output as "true" ?
Shall I add an explicit "return 1;" ?
does the sub actually return the last evaluation, in this case the sucess of the
test in the "if"? Something else I did not think about but which is
obvious to everybody?
In this case, I'd just check to ensure that the object's attribute was set correctly. That's all this particular sub does. If it's set ok, the sub ended correctly. If it wasn't set, something went wrong before the sub ended.
my $p='blah';
$obj->my_sub($p);
is $obj->{field}, $p, "my_sub() set the field attr ok";
It would be better if the field attribute had a getter so you're not breaking encapsulation, but I digress.
A sub that has no need to return a value should end with
return;
In your case, without it, you will be returning the value of $possible_value, which is the last thing executed. This doesn't look like a useful thing to return.
Assuming you add the explicit return:
Your throws_ok test looks fine. You should then test that the field was correctly set. Your ok test isn't needed, since your sub won't be returning anything.
Perl returns the result of the last executed code by default.
For example:
print main();
sub main {
my $var = 9 * 7;
}
print will output 63. If your code may be affected by the output of a given subroutine, then you need to set a return value (it's generally considered a best practice to always set an explicit return at the end of a subroutine/method).
print main();
sub main {
my $var = 9 * 7;
return;
}
print will output nothing.
Personally, I always try to set a return value depending on the context of what the subroutine will be returning to, but if you're writing code other people will be using, then it's generally safest to just do return;.
An additional explanation from Perl::Critic (link to the specific policy):
Subroutine "main" does not end with "return" at line 8, near 'sub main {'.
Subroutines::RequireFinalReturn (Severity: 4)
Require all subroutines to terminate explicitly with one of the
following: return',carp', croak',die', exec',exit', goto', or
throw'.
Subroutines without explicit return statements at their ends can be
confusing. It can be challenging to deduce what the return value will
be.
Furthermore, if the programmer did not mean for there to be a
significant return value, and omits a return statement, some of the
subroutine's inner data can leak to the outside. Consider this case:
package Password;
# every time the user guesses the password wrong, its value
# is rotated by one character
my $password;
sub set_password {
$password = shift;
}
sub check_password {
my $guess = shift;
if ($guess eq $password) {
unlock_secrets();
} else {
$password = (substr $password, 1).(substr $password, 0, 1);
}
}
1;
In this case, the last statement in check_password() is the assignment.
The result of that assignment is the implicit return value, so a wrong
guess returns the right password! Adding a `return;' at the end of that
subroutine solves the problem.
The only exception allowed is an empty subroutine.
Be careful when fixing problems identified by this Policy; don't blindly
put a `return;' statement at the end of every subroutine.

perl function ignores undef arguments

I'm passing some arguments to a function, one of which might be undefined.
$a = ($config->function(param('a'),'int'));
my module contains a function which looks like this:
sub function{
my $self = $_[0];
my $t = $_[1];
my $type = $_[2];
print "$self,$t,$type<br/>";
}
I've tried with shift instead of the #_ syntax, but there's no change.
The problem is that if $config->function is called with an undefined param('a') it prints like this:
MY_FUNC=HASH(0x206e9e0),name,
it seems that $t is being set to the value of what $type should be and the undef is being ignored completely.
undef is perfectly valid in a parameter list. I suspect that the problem here is that the param function is returning an empty list, rather than undef.
sub function {
no warnings 'uninitialized';
print join "/", #_;
}
function(undef,"foo"); # outputs "/foo"
function((), "foo"); # outputs "foo"
In the argument list to function, the param function is evaluated in list context.
sub param1 {
return; # undef in scalar context, but empty list in list context
}
sub param2 {
return undef; # scalar ctx: undef, list ctx: list with one undef elem
}
function(param1(), "foo"); # param1() -> () ... outputs "foo"
function(param2(), "foo"); # param2() -> (undef) ... outputs "/foo"
A workaround is to make sure that your param function is evaluated in scalar context.
function(scalar param1(), "foo"); # now outputs "/foo"
Note that actually saying return undef in your Perl subroutines is considered by some to be a code smell.
The CGI param method is correctly returning an empty list in the case of an error. This is standard behaviour for Perl subroutines and methods, which return a false value in the case of an erorr.
As #mob says, an explicit return undef is wrong precisely because it returns a one-element list in list context. This is a true value and implies success, which would break a lot of code if it was changed
All you need to do is change your code to extract the parameter separately
my $param = param('a');
$config->function($param, 'int'));
or if you're really keen to put the call in the parameter list then you can write
$config->function(scalar param('a'), 'int'));
or
$config->function(param('a') // undef, 'int'));
[I started this as a comment but it got too long].
Inside an argument list, arguments are evaluated in list context. Two answers have already noted that, but I think you missed it for all of the other things they were talking about.
Beyond that, param has to handle at least three cases, and the interface Lincoln chose, although sometimes annoying, is a good enough way to handle them.
The query parameter a does not appear at all:
b=1
The query parameter a appears, but with no value:
a=&b=1
The query parameter a appears with a value:
a=1&b=1

Is there a simple way to validate a hash of hash element exists and is defined?

I need to validate a Perl hash of hash element such as $Table{$key1}{$key2} to exist and be defined. Here is what I do. (I have no idea $key1 even exists)
if
((defined $Table{$key1}) &&
(exists $Table{$key1}) &&
(defined $Table{$key1}{$key2}) &&
(exists $Table{$key1}{$key2}))
{
#do whatever
}
Is there an easier and cleaner way to do it?
You don't need to check each level of the heirarchy: you can just go for the value you care about. exists doesn't check for definedness, only if the slot in the hash exists (it could exist with an undefined value), so if you care that the value is defined, you would need to call defined rather than exists. If a value is not defined, it evaluates in boolean context to false, so we can type a little less and reduce your example to:
if ($Table{$key1}{$key2})
{
# do whatever
}
However, if the value in that key is defined but is "false" (numerically evaluates to zero, or is the empty string), this can cause a false negative, so we should explicitly check for definedness if this is a possibility:
if (defined $Table{$key1}{$key2})
{
# do whatever
}
If you don't want to autovivify $Table{$key1}, you can check for its existence first, which brings us to the "best" way for the general case:
if (exists $Table{$key1} and defined $Table{$key1}{$key2})
{
# do whatever
}
If you're going to do this a lot for various fields in a hash, you may want to add some OO-style accessor methods which would do this work for you:
sub has_field
{
my ($this, $fieldName) = #_;
return exists $this->{data} && defined $this->{data}{$fieldName});
}
I'm sure you've read it already, but it can't hurt to read the relevant documentation again:
perldoc -f exists
perldoc perldata
perldoc perldsc
Given an expression that specifies a hash element or array element, exists returns true if the specified element in the hash or array has ever been initialized, even if the corresponding value is undefined. The element is not autovivified if it doesn't exist.
...
A hash or array element can be true only if it's defined, and defined if it exists, but the reverse doesn't necessarily hold true.
The following is shorter and will protect from autovivifcation:
if (exists $table{$key1} and defined $table{$key1}{$key2}) {...}
The other checks in your code are not needed.
Check existence first, then defined-ness. (A value can exist without being defined but not be defined without existing.) You should test the intermediate levels with exists to prevent unintended autovivification. For the last level you only need to call defined. When there aren't too many layers it's easy to code directly:
if (exists $hash{a} && defined $hash{a}{b}) {...}
This gets awkward if there are many layers:
if (exists $hash{a} && exists $hash{a}{b} && exists $hash{a}{b}{c} ...) {...}
In that case, you can write a version of defined that doesn't autovivify intermediate values:
sub safe_defined {
my $h = shift;
foreach my $k (#_) {
if (ref $h eq ref {}) {
return unless exists $h->{$k};
$h = $h->{$k};
}
else {
return;
}
}
return defined $h;
}
You use it this way:
if (safe_defined(\%hash, qw(a b c))) {
say $hash{a}{b}{c};
}
Note: This version of the function is limited.
It only handles nested hashes. Perl lets you construct arbitrary data
structures, like a hash of arrays of scalar references...
It doesn't support blessed references (i.e. objects).
A truly generic version is left as an exercise for the reader. ;)
You could check out Data::Diver. It dives into data structures without autovivifying. The syntax would be:
if ( defined Dive(\%Table, $key1, $key2) ) { ... }
or even:
if ( defined(my $value = Dive(\%Table, $key1, $key2) ) ) {
...do something with $value...
}
Great! Thanks you all for the reply.
Since the autovivifying is an issue for me, currently i am using the "awkward" approach, i.e.
if (exists $Table{$key1} && defined $Table{$key1}{$key2}) {
Do whatever
}
It works for me, however as you guys said, i have 3-4 level deep of nested hash, the code is bit of messy.
I will check out Data:Diver. That one looks nicer.
Thanks, again,