perlcritic: eval "require $module"; - perl

While digging in some old source code I saw the following:
my $module = $some{module};
eval "require $module";
die "Bad module\n$#" if $#;
while I understand what the code does, it tries "require" a module and die when it is unsuccessful - the perlcritic complains about it
Expression form of "eval" at line 331, column 13. See page 161 of
PBP. (Severity: 5)
Unfortunately I havent the PBP book, so wondering what is the correct method of the above...
Also, in the same source found:
sub test_repo_file {
my($self, $repo, $test) = #_;
my $abspath = repo_abs_path($repo);
return "eval -$test $abspath";
}
Here doesn't understand what solves the "eval", and the perlcritic complains again for the "string eval"...
Can somebody please explain the basic points about the "string eval" and how to write the above correctly?

Running perlcritic --verbose '%d\n' will give you the explanations, too:
The string form of `eval' is recompiled every time it is executed,
whereas the block form is only compiled once. Also, the string form
doesn't give compile-time warnings.
eval "print $foo"; # not ok
eval {print $foo}; # ok
It's applicable to the first case.
The second case doesn't generate any messages for me. Isn't it rather
return eval "-$test $abspath"
You can't use block eval here. You should verify that $test really contains what it should
$test =~ /^[a-z]$/i
and avoid the evaluation of $abspath:
eval "-$test \$abspath"
If you're OK with that, you can than add
## no critic
to the end of the line.

Few users should ever have to use eval EXPR. Most of the time, it's being used as a template system when it shouldn't (e.g. s/.../eval($repl)/e), or it's used to catch exceptions when eval BLOCK should have been used.
If there is reason to use it, it's to execute generated code or user-submitted code. Generating code is tricky and error-prone, and errors have security implications. Executing user-submitted code is a major security concern. As such, every use of eval EXPR should be carefully reviewed.
It's quite appropriate for perlcritic to flag its usage given that virtually every use is an error with major security implications.
In your case, the use of eval EXPR is suboptimal. I'd use
my $path = $module . ".pm";
$path =~ s{::}{/}g;
eval { require $path }
Yes, this is portable.

Related

Perl Script (for calling Mac::AppleScript Perl module)

I'm setting up some automation using shell and applescript scripting. Several of my modules require passing several params into the AppleScript. Although there are a few ways of doing so, a bit of research lead me to the "Mac::AppleScript Perl module" and it looks really promising. However it requires a short .pl script in /usr/local/bin. The only example of it I found has two syntax errors and I have absolutely no experience at all in Perl.
I've tried guessing at what might be throwing the errors based on what I would use in the languages I've worked with but even though I can recognize the variables not expanding in a few spots, my attempts at correcting it results in further errors and I've been hacking at it for hours. Can someone please take a look and tell me what might be the syntax issues I can fix?
#!/usr/bin/perl
use strict;
use Mac::AppleScript qw/ RunAppleScript /;
if( $#ARGV = 0) ? '"'.join('","',#ARGV).'"' : "";
my $rtn = RunAppleScript( "return run script alias ((POSIX file ""
.$script.'") as text) with parameters {'.$args.'}' ),"n"
or die "AppleScript Error: $!";
$rtn =~ s/(^"|"$)//g;
print $rtn,"n";
Problems:
You have an unescaped " in your double-quoted string literal.
You use variables you never declared or initialized.
You have a stray ,"n" in your code.
Your code suffers from code injection bugs.
The error message is found in $#, not $!.
Fixed:
sub text_to_as_lit { '"'.( $_[0] =~ s/([\\"])/\\$1/rg ).'"' } #'
my ($script, #args) = #ARGV;
my $rtn = RunAppleScript(sprintf(
"return run script alias ((POSIX file %s) as text) with parameters {%s}",
text_to_as_lit($script),
( join ',', map text_to_as_lit($_), #args ),
))
or die("AppleScript Error: $#");
Untested.

in perl, 'use strict' disables important warnings

Adding use strict to a perl program disables an important warning message. Is this a bug in perl?
This little program has a problem
#!/usr/bin/perl -w
my $rule;
sub applyRule() {
eval $rule;
}
my $foo_bar;
$foo_bar = "mega-foo-bar-ness";
$rule = 's/#FOO_BAR/$foo_bar/g';
$_ = "Please replace #FOO_BAR with something.";
applyRule();
print STDERR "OUTPUT: $_\n";
and we get a helpful warning message when running that program:
$ ./test1.pl
Use of uninitialized value $foo_bar in substitution (s///) at (eval 1) line 1.
OUTPUT: Please replace with something.
Now add use strict;
#!/usr/bin/perl -w
use strict;
my $rule;
sub applyRule() {
eval $rule;
}
my $foo_bar;
$foo_bar = "mega-foo-bar-ness";
$rule = 's/#FOO_BAR/$foo_bar/g';
$_ = "Please replace #FOO_BAR with something.";
applyRule();
print STDERR "OUTPUT: $_\n";
The substitution just silently fails.
$ ./test1.pl
OUTPUT: Please replace #FOO_BAR with something.
When I have mysterious errors in perl should I always try removing use strict to see if I get some helpful warnings?
edit 1 stackoverflow is asking me to describe why this is not a duplicate of Why use strict and warnings?. This is a case where adding use strict results in fewer warnings, which makes debugging harder -- the opposite of what I expect use strict would do; the opposite of what is suggested in the answers to that question:
The pragmas catch many errors sooner than they would be caught otherwise, which makes it easier to find the root causes of the errors.
edit 2: For anyone else seeing this: another way is:
defined (eval $rule) or warn "eval error: $#\n";
because eval returns undefined if there is an error. The Programming Perl book states in the section about eval
If there is a syntax error or run-time error, eval returns the undefined value and puts the error message in $#. If there is no error, $# is guaranteed to be set to the null string, so you can test it reliably afterward for errors.
Note however that with versions prior to Perl 5.14 there can be problems relying on '$#`.
Instead of a warning, it throws an exception. And you aren't testing to see if eval gave an exception.
Try:
eval "$rule; 1" or warn "exception thrown: $#\n";

What is $# in Perl?

I have a Perl script which I cannot understand.
for $i(#myarr)
{
#some stuff
eval {
#some stuff
};
if($#)
{
print "*** $# ****";
}
}
What does that eval do? Is it a standard Perl command or just some sub?
What is the variable $#? It is currently printing a string but I don know where that string is coming from.
$# The Perl syntax error or routine error message from the last eval, do-FILE, or require command. If set, either the compilation failed, or the die function was executed within the code of the eval. please read this doc http://perldoc.perl.org/perlvar.html
To add to Suic’s answer, see the English module that lets you use more descriptive $EVAL_ERROR instead of $# and the Try::Tiny or TryCatch modules that avoid common traps associated with using eval for exception handling. Also, the next time you wonder about a Perl function, perldoc -f is your friend (like perldoc -f eval).

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

How to check if variable is declared in perl?

I am using use strict; in perl and with that I use the following statement.
unless(defined($x)){
print "Not defined";
}
Where $x is not declared anywhere. So I expect it to print "Not defined" but it returns an error
Global symbol "$x" requires explicit package name at *********** in line 15.
The strict pragma has three parts: strict references, strict variables, and strict subs. The one you're running into is
strict vars
This generates a compile-time error if you access a variable that wasn't declared via our or use vars, localized via my, or wasn't fully qualified. Because this is to avoid variable suicide problems and subtle dynamic scoping issues, a merely local variable isn't good enough.
Because it generates compile-time errors, your non-BEGIN code won't even have a chance to run. You can temporarily allow non-strict variables inside a block as in
{
no strict 'vars';
print "Not defined!\n" unless defined $x;
}
but note that Perl's defined operator tells you whether a value is defined, not whether a variable has been declared.
Tell us more about your application, and we can give you better advice about how to handle it.
You can't even refer to a variable unless it's declared. When you ask
defined( $x ) ?
the compiler is going to complain: I don't know this what you're asking about, how am I supposed to tell it is defined? It has no point of reference for that variable, since you've indicated you do not want variables auto-created by name.
If strict 'vars' was not on--which it is by default when you use strict--then it would create an entry in the package symbol table for 'x'.
Interestingly enough is that without strict 'refs' it is also easy to check if a variable is in the package symbol table.
defined( *{ __PACKAGE__ . '::x' }{SCALAR} )
Since there is no way to auto-create a lexical ("my variables"), there is also not a standard way to check to see if lexicals are declared. Lexical variables are stored in the "pad". But there is a module PadWalker that can help.
In order to check the current level, you could get a hash of the pad, and then check whether or not it exists in the current pad. You could also loop back up through the stack (the integer argument works something like caller) to find where the most recent x was.
my $h = peek_my (0);
exists $h->{x};
I think you are mixing 'defined' and 'declared' concepts.
Your are asking for 'How to check if variable is declared in perl' but then you are checking if a variable is defined. These are two different concepts.
In perl if you use 'use strict' you are automatically checking for any variable not declared (using my, local or our). Once you have a variable declared, you can test if it is defined (have a value assigned).
So in your test, you are missing a prior declaration before testing for defineness
use strict;
my $x; # you are missing this part
[...] | # code
# your test for define
print defined $x? "defined\n" : "not defined\n";
Please be aware the testing for only $x is incorrect for your purpose:
my ($x,$y, $z);
$w; # not declared (use strict will catch it and die)
$x = 0; # declared and defined BUT if you make a logic test like 'if ($x) {}' then it will be FALSE, so don't confuse testing for **'$x'** and testing for **'defined $x'**
$y = undef; # declared but not defined
$z = 1; # declared, defined, and logial test TRUE
Finally the answer of xenorraticide seems faulty to me: he suggest 'unless $x' that is not correct for testing if defined as I said before. He also suggest 'unless exists $x', that is wrong for testing scalars. 'exists' test is only for hashes keys (and deprecated for arrays).
Hope this helps.
#
print "Not defined" if !defined($x);
result will be
Not defined
#
use strict;
print "Not defined" if !defined($x);
will generate error as in your question.
Look to: http://perldoc.perl.org/strict.html, where described how you can import only required restrictions. (However use strict 'vars' is very good idea :) )
Normally this kind of code should not be required for a serious program, but still why not just for fun: (assuming use strict)
print "Not defined\n" unless eval 'ref(\$x)';
#!/usr/bin/perl -l
use strict;
# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'
our $lol = eval {$lol} || 'lol' ;
print $lol;
my solution is
#!/usr/bin/perl -l
use strict;
# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'
our $lol = eval {$lol} || 'lol' ;
print $lol;
You can use unless, like this:
use 'strict';
my $var = 'defined';
unless (defined($var)) {
print "not defined\n";
}
$var = undef;
unless (defined($var)) {
print "not defined\n";
}
The first print will not print anything, the second one will since $var has been made undef.