why can't I use this in my subroutine? - perl

I have the following:
$sql="SELECT * FROM table WHERE name = ? ";
&checkDB( $sql , "bob" , "while" );
sub checkDB(){
my $sth=$dbh->prepare($_[0]) or warn "$DBI::errstr";
$sth->execute( $_[1] ) or warn "$DBI::errstr";
print $_[2] . "\n"; # this works
$_[2] ( my #rows= $sth -> fetchrow() ) { # this doesn't work
blah, blah, blah
}
}
I pass my sql statement, 'bob' and either a "while" or "unless" variable into the subroutine.
My subroutine will let me pass the "while" variable (it will print "while\n") but it won't let me use it to fetchrows. What am I doing wrong?
my exact error is "syntax error at script.pl near "}" "....works fine if I substitute $_[2] with the actual word "while"

Because while and "while" are different things.
You can't substitute arbitrary language constructs for strings.

It is not possible to use variable in place of keyword in perl. If you really must do such constructs, look at eval - it lets you run arbitrary string as perl code.

I think what you would need here is an eval, however this has evil written all over it. You definitely want to be sure that nothing in that code is tainted (coming from user). I think you probably should think very hard about why you would want to do this and if there isn't a better way (two different functions for example). I know we programmers like to be lazy, but evals are dangerous.

Related

Why can't I concatenate and print the value returned by a function?

I have a Perl program that calls a function, in this case ref, and checks the result. Specifically, I am testing that a variable is a hash reference. In that case, ref will return 'HASH'. I tested it and it worked.
Then I decided to log it, adding a print that displays the result of the same call, but it didn't work correctly. Here is a reduced version:
use strict;
use warnings;
my $book_ref = {};
$book_ref->{'title'} = 'The Lord of the Rings';
if (ref $book_ref eq 'HASH') {
print "ref \$book_ref is a " . ref $book_ref . "\n";
}
print "Program is over\n";
To my surprise, this was the output:
ref $book_ref is a Program is over
And despite using strict and warnings there were neither errors nor warnings.
The call to ref is exactly the same (it's a copy and paste), but while it works correctly inside the if condition, print doesn't display anything, and actually seems to be interrupted, as the newline character is clearly skipped. Why does the behaviour change?
The reason is that the function ref is called without parentheses, and this causes the line to be parsed incorrectly.
When ref is called inside the if, the condition is clearly delimited by parentheses, which means that ref knows perfectly well what its argument is: $book_ref. There's no ambiguity.
Instead, when printing the result, the lack of parentheses means that Perl will parse the line in a way that was not intended:
First it will concatenate $book_ref and "\n". In scalar context, $book_ref evaluates to a string like HASH(0x1cbef70), therefore the result is the string "HASH(0x1cbef70)\n"
Then, ref will be called on the string "HASH(0x1cbef70)\n", producing as output an empty string: ''.
At this point, print prints the empty string, that is, nothing, and stops there. The newline character \n is skipped because it has already been consumed by ref, so print doesn't even see it. And there are no errors.
All of this descends from Perl's operator precedence: from the table,
(...)
8. left + - .
9. left << >>
10. nonassoc named unary operators
11. nonassoc < > <= >= lt gt le ge
12. nonassoc == != <=> eq ne cmp ~~
(...)
where the "function call" is actually a "named unary operator" (the unary operator being ref). So the . operator at line 8 has higher precedence than the function call at line 10, which is why the result of print is not the expected one. On the other hand, the function call has higher precedence than eq (at line 12), which is why inside the if everything works as expected.
The solution to precedence problems is to use .
A possibility is to use parentheses to emphasize the function call:
print "ref \$book_ref is a " . ref($book_ref) . "\n";
Another one, which I like less but nevertheless works, is to use parentheses to isolate the string that must be concatenated, by putting the opening bracket just before ref:
print "ref \$book_ref is a " . (ref $book_ref) . "\n";
Another possible approach, suggested by zdim in a comment, is to use commas:
print "ref \$book_ref is a ", ref $book_ref, "\n";
When I first wrote the if I decided to avoid the parentheses to make the code more readable. Then I copied it and didn't notice the problem. I ended up wasting 2 hours to find the bug.
The best solution seems to be the first one, because if you copy it to another place (like another print) you are guaranteed to also copy the parentheses that prevent the problem. With the second one I probably wouldn't realize how important the parentheses are and wouldn't copy them. And the third one works only if you remember that you have to always use commas and not dots, which is not obvious and therefore error prone. So, although they work, I consider them less safe.
Other comments have also suggested to use printf, which requires dealing with format specifiers, or expression interpolation, like print "ref \$book_ref is a ${\ ref $book_ref }\n";, which I find harder to read.
Bottom line: always use the parentheses.

Why does Perl sub s require &

The following file does not compile:
sub s {
return 'foo';
}
sub foo {
my $s = s();
return $s if $s;
return 'baz?';
}
The error from perl -c is:
syntax error at foobar.pl line 5 near "return"
(Might be a runaway multi-line ;; string starting on line 3)
foobar.pl had compilation errors.
But if I replace s() with &s() it works fine. Can you explain why?
The & prefix definitively says you want to call your own function called "s", rather than any built-in with the same name. In this case, it's confusing it for a substitution operator (like $stuff =~ s///;, which can also be written s()()).
Here's a PerlMonks discussion about what the ampersand does.
The problem you have, as has already been pointed out, is that s() is interpreted as the s/// substitution operator. Prefixing the function name with an ampersand is a workaround, although I would not say necessarily the correct one. In perldoc perlsub the following is said about calling subroutines:
NAME(LIST); # & is optional with parentheses.
NAME LIST; # Parentheses optional if predeclared/imported.
&NAME(LIST); # Circumvent prototypes.
&NAME; # Makes current #_ visible to called subroutine.
What the ampersand does here is merely to distinguish between the built-in function and your own.
The "proper" way to deal with this, apart from renaming your subroutine, is to realize what's going on under the surface. When you say
s();
What you are really saying is
CORE::s();
When what you mean is
main::s();
my $s = 's'->();
works too--oddly enough with strict on.

How to tell perl to print to a file handle instead of printing the file handle?

I'm trying to wrap my head around the way Perl handles the parsing of arguments to print.
Why does this
print $fh $stufftowrite
write to the file handle as expected, but
print($fh, $stufftowrite)
writes the file handle to STDOUT instead?
My guess is that it has something to do with the warning in the documentation of print:
Be careful not to follow the print keyword with a left parenthesis unless you want the corresponding right parenthesis to terminate the arguments to the print; put parentheses around all arguments (or interpose a + , but that doesn't look as good).
Should I just get used to the first form (which just doesn't seem right to me, coming from languages that all use parentheses around function arguments), or is there a way to tell Perl to do what I want?
So far I've tried a lot of combination of parentheses around the first, second and both parameters, without success.
On lists
The structure bareword (LIST1), LIST2 means "apply the function bareword to the arguments LIST1", while bareword +(LIST1), LIST2 can, but doesn't neccessarily mean "apply bareword to the arguments of the combined list LIST1, LIST2". This is important for grouping arguments:
my ($a, $b, $c) = (0..2);
print ($a or $b), $c; # print $b
print +($a or $b), $c; # print $b, $c
The prefix + can also be used to distinguish hashrefs from blocks, and functions from barewords, e.g. when subscripting an hash: $hash{shift} returns the shift element, while $hash{+shift} calls the function shift and returns the hash element of the value of shift.
Indirect syntax
In object oriented Perl, you normally call methods on an object with the arrow syntax:
$object->method(LIST); # call `method` on `$object` with args `LIST`.
However, it is possible, but not recommended, to use an indirect notation that puts the verb first:
method $object (LIST); # the same, but stupid.
Because classes are just instances of themselves (in a syntactic sense), you can also call methods on them. This is why
new Class (ARGS); # bad style, but pretty
is the same as
Class->new(ARGS); # good style, but ugly
However, this can sometimes confuse the parser, so indirect style is not recommended.
But it does hint on what print does:
print $fh ARGS
is the same as
$fh->print(ARGS)
Indeed, the filehandle $fh is treated as an object of the class IO::Handle.
(While this is a valid syntactic explanation, it is not quite true. The source of IO::Handle itself uses the line print $this #_;. The print function is just defined this way.)
Looks like you have a typo. You have put a comma between the file handle and the argument in the second print statement. If you do that, the file handle will be seen as an argument. This seems to apply only to lexical file handles. If done with a global file handle, it will produce the fatal error
No comma allowed after filehandle at ...
So, to be clear, if you absolutely have to have parentheses for your print, do this:
print($fh $stufftowrite)
Although personally I prefer to not use parentheses unless I have to, as they just add clutter.
Modern Perl book states in the Chapter 11 ("What to Avoid"), section "Indirect Notation Scalar Limitations":
Another danger of the syntax is that the parser expects a single scalar expression as the object. Printing to a filehandle stored in an aggregate variable seems obvious, but it is not:
# DOES NOT WORK AS WRITTEN
say $config->{output} 'Fun diagnostic message!';
Perl will attempt to call say on the $config object.
print, close, and say—all builtins which operate on filehandles—operate in an indirect fashion. This was fine when filehandles were package globals, but lexical filehandles (Filehandle References) make the indirect object syntax problems obvious. To solve this, disambiguate the subexpression which produces the intended invocant:
say {$config->{output}} 'Fun diagnostic message!';
Of course, print({$fh} $stufftowrite) is also possible.
It's how the syntax of print is defined. It's really that simple. There's kind of nothing to fix. If you put a comma between the file handle and the rest of the arguments, the expression is parsed as print LIST rather than print FILEHANDLE LIST. Yes, that looks really weird. It is really weird.
The way not to get parsed as print LIST is to supply an expression that can legally be parsed as print FILEHANDLE LIST. If what you're trying to do is get parentheses around the arguments to print to make it look more like an ordinary function call, you can say
print($fh $stufftowrite); # note the lack of comma
You can also say
(print $fh $stufftowrite);
if what you're trying to do is set off the print expression from surrounding code. The key point is that including the comma changes the parse.

Perl passing argument into eval

I'm facing an issue using eval function.
Indeed I have some function name inside a SQL database, my goal is to execute those functions within perl (after retrieve in SQL).
Here is what I'm doing, considering that $RssSource->{$k}{Proceed} contains "&test" as a string retrieved from SQL:
my $str2 = "ABCD";
eval "$RssSource->{$k}{Proceed}";warn if $#;
sub test
{
my $arg = shift;
print "fct TEST -> ", $row, "\n";
}
This is working correctly and display:
fct TEST ->
However I would like to be able to pass $str2 as an argument to $RssSource->{$k}{Proceed} but I don't know how, every syntax I tried return an error:
eval "$RssSource->{$k}{Proceed}$str2"
eval "$RssSource->{$k}{Proceed}($str2)"
eval "$RssSource->{$k}{Proceed}"$str2
eval "$RssSource->{$k}{Proceed}"($str2)
May someone tell me how to properly pass an argument to the evaluated function?
Thanks a lot for your help
Regards.
Florent
I'm going to assume that $RssSource->{$k}{Proceed} always contain name or &name, otherwise what you are asking doesn't make much sense.
my $func_name = $RssSource->{$k}{Proceed};
$func_name =~ s/&//;
my $func_ref = \&$func_name; # Works with strict on!
$func_ref->(#args);
If you want to add some error checking, the following will check if the sub can be called:
defined(&$func_ref)
If the string you are evaling always is a sub invocation, you can construct the eval string in one of these ways:
$RssSource->{$k}{Proceed} . '($str2)'
(most general), or
$RssSource->{$k}{Proceed} . "(\"$str2\")"
(inelegant)
Here are the problems your solutions ran into:
eval "$RssSource->{$k}{Proceed}$str2" evaluates to eval "&testABCD". This sub doesn't exist.
eval "$RssSource->{$k}{Proceed}($str2)" evaluates to "&test(ABCD)". Bareword not allowed.
eval "$RssSource->{$k}{Proceed}"$str2 A string has to be followed by some sort of operator, not another variable.
eval "$RssSource->{$k}{Proceed}"($str2) You are trying to call a string as a function. This is not supported in Perl.
If you can change the data in your database to contain just a function name, that is, test rather than &test, you can call a function by a symbolic reference, rather than using eval:
$fn="test";
&{$fn}("argument")
You do not need eval if, as you say, your database just contains function names. You can use them as symbolic references (but please remove the &). The modern way to do it would not be using the & to dereference it but to use the arrow operator:
{
no strict 'refs'; # hopefully you have strict on already...
$RssSource->{$k}{Proceed}->($str2);
}
Very similar to ikegami's answer, using the can method, which is more my taste. TIMTOWTDI.
my $func_name = $RssSource->{$k}{Proceed};
$func_name =~ s/&//;
my $func_ref = __PACKAGE__->can($func_name)
or die "No function named $func_name";
$func_ref->(#args);

perl encapsulate single variable in double quotes

In Perl, is there any reason to encapsulate a single variable in double quotes (no concatenation) ?
I often find this in the source of the program I am working on (writen 10 years ago by people that don't work here anymore):
my $sql_host = "something";
my $sql_user = "somethingelse";
# a few lines down
my $db = sub_for_sql_conection("$sql_host", "$sql_user", "$sql_pass", "$sql_db");
As far as I know there is no reason to do this. When I work in an old script I usualy remove the quotes so my editor colors them as variables not as strings.
I think they saw this somewhere and copied the style without understanding why it is so. Am I missing something ?
Thank you.
All this does is explicitly stringify the variables. In 99.9% of cases, it is a newbie error of some sort.
There are things that may happen as a side effect of this calling style:
my $foo = "1234";
sub bar { $_[0] =~ s/2/two/ }
print "Foo is $foo\n";
bar( "$foo" );
print "Foo is $foo\n";
bar( $foo );
print "Foo is $foo\n";
Here, stringification created a copy and passed that to the subroutine, circumventing Perl's pass by reference semantics. It's generally considered to be bad manners to munge calling variables, so you are probably okay.
You can also stringify an object or other value here. For example, undef stringifies to the empty string. Objects may specify arbitrary code to run when stringified. It is possible to have dual valued scalars that have distinct numerical and string values. This is a way to specify that you want the string form.
There is also one deep spooky thing that could be going on. If you are working with XS code that looks at the flags that are set on scalar arguments to a function, stringifying the scalar is a straight forward way to say to perl, "Make me a nice clean new string value" with only stringy flags and no numeric flags.
I am sure there are other odd exceptions to the 99.9% rule. These are a few. Before removing the quotes, take a second to check for weird crap like this. If you do happen upon a legit usage, please add a comment that identifies the quotes as a workable kludge, and give their reason for existence.
In this case the double quotes are unnecessary. Moreover, using them is inefficient as this causes the original strings to be copied.
However, sometimes you may want to use this style to "stringify" an object. For example, URI ojects support stringification:
my $uri = URI->new("http://www.perl.com");
my $str = "$uri";
I don't know why, but it's a pattern commonly used by newcomers to Perl. It's usually a waste (as it is in the snippet you posted), but I can think of two uses.
It has the effect of creating a new string with the same value as the original, and that could be useful in very rare circumstances.
In the following example, an explicit copy is done to protect $x from modification by the sub because the sub modifies its argument.
$ perl -E'
sub f { $_[0] =~ tr/a/A/; say $_[0]; }
my $x = "abc";
f($x);
say $x;
'
Abc
Abc
$ perl -E'
sub f { $_[0] =~ tr/a/A/; say $_[0]; }
my $x = "abc";
f("$x");
say $x;
'
Abc
abc
By virtue of creating a copy of the string, it stringifies objects. This could be useful when dealing with code that alters its behaviour based on whether its argument is a reference or not.
In the following example, explicit stringification is done because require handles references in #INC differently than strings.
$ perl -MPath::Class=file -E'
BEGIN { $lib = file($0)->dir; }
use lib $lib;
use DBI;
say "ok";
'
Can't locate object method "INC" via package "Path::Class::Dir" at -e line 4.
BEGIN failed--compilation aborted at -e line 4.
$ perl -MPath::Class=file -E'
BEGIN { $lib = file($0)->dir; }
use lib "$lib";
use DBI;
say "ok";
'
ok
In your case quotes are completely useless. We can even says that it is wrong because this is not idiomatic, as others wrote.
However quoting a variable may sometime be necessary: this explicitely triggers stringification of the value of the variable. Stringification may give a different result for some values if thoses values are dual vars or if they are blessed values with overloaded stringification.
Here is an example with dual vars:
use 5.010;
use strict;
use Scalar::Util 'dualvar';
my $x = dualvar 1, "2";
say 0+$x;
say 0+"$x";
Output:
1
2
My theory has always been that it's people coming over from other languages with bad habits. It's not that they're thinking "I will use double quotes all the time", but that they're just not thinking!
I'll be honest and say that I used to fall into this trap because I came to Perl from Java, so the muscle memory was there, and just kept firing.
PerlCritic finally got me out of the habit!
It definitely makes your code more efficient, but if you're not thinking about whether or not you want your strings interpolated, you are very likely to make silly mistakes, so I'd go further and say that it's dangerous.