Perl does not complain about missing semicolon - perl

I just found on my Ubuntu that Perl is not complaining about the semicolon at the end. Check the following code:
#!/usr/bin/perl
use warnings;
use strict;
my #array = (1, 2, 3, 4);
foreach (#array)
{
print $_."\n"
}
print "no, this cant be true"
Please notice that semicolon ";" is missing from the print statement. Still the code runs fine.
OUTPUT:
1
2
3
4
no, this cant be true
If I put semicolon after print, it still works. So this is confusing to me.
Could you help me understand what am I missing here, OR is there some obvious Perl ideology that I overlooked?

From perldoc perlsyn:
Every simple statement must be terminated with a semicolon, unless it is the final statement in a block, in which case the semicolon is optional.
Your print statement is the last statement in a block.
Omitting the semi-colon isn't recommended though. It's too easy to forget to add it if you extend the block later.

I often think of semicolons in Perl as separators rather than terminators - that makes this behaviour a lot easier to get used to.
That said, it's not at all a bad idea to always use a semicolon as you don't have to remember to add it later if you put more statements at the end of the block, a bit like using an extra comma in a list so that you don't forget to add that later (Perl ignores the last comma if there's no list item after it).

From the Perl documentation:
Every simple statement must be terminated with a semicolon, unless it
is the final statement in a block, in which case the semicolon is
optional.

Related

Should I use parenthesis in postfix `if` syntax?

Are there any benefits to use parenthesis in postfix if statement? like:
return 1 if ($self->found());
One of my workmate stick to use parenthesis and It's not common for me. I would appreciate if someone point out differences.
Following is output from perl -MO=Deparse
#!/usr/bin/perl
use strict;
my $found = 1;
print "FOUND\n" if $found;
print "FOUND\n" if ($found);
__END__
test.pl syntax OK
use strict;
my $found = 1;
print "FOUND\n" if $found;
print "FOUND\n" if $found;
Parens are never needed around the condition in the if statement modifier, so consistency and playing-it-safe aren't factors. (Same goes for the other statement modifiers.)
I don't see any advantage to using them. In fact, I find them a bit jarring.
I believe most experienced Perl programmers don't use them, to the point that it's a fair indicator of "newbieness".
It's mainly a matter of style. There's no requirement to use parens around the entire conditional expression and it has no effect on the way the code behaves, so decide for yourself what standard you want to use. Personally, I don't, and my impression is that most Perl programmers don't either, but that could just be selection bias.
I will note, though, that any time I start wanting to use parens within the conditional expression, that's a pretty good sign that it shouldn't be in a postfix clause, so then I put parens around the whole thing and change it to the if (...) {...} form.
If you want to use Perl Best Practices, you should only postfix if for control of flow (break, next, last). When you use postfix if you should probably have easy enough if-statements to not need parenthesis.
For If blocks I think the best practice is to use parenthesis.
I usually use the parentheses because more often than not I guess wrong on how many statements I need to be conditional. Then I have to rewrite the line to become a proper if (condition) { BLOCK }. Having the parentheses already there make this slightly easier.
There's also the mental/readability benefit of having all if statements look the same.

Disable Perl warnings for missing qw outer parentheses

Since upgrading Debian recently I've had Perl whining about not having extra parentheses around the qw operator.
As a Systems Administrator this is unacceptable. It is breaking mod_perl applications left, right, and centre.
How can I run Perl with this warning disabled? Is there a flag I can run with the Perl interpreter? Note that editing source is not an option.
This changed in Perl 5.14 - see perldelta.
Use of qw(...) as parentheses
Historically the parser fooled itself into thinking that qw(...)
literals were always enclosed in parentheses, and as a result you
could sometimes omit parentheses around them:
for $x qw(a b c) { ... }
The parser no longer lies to itself in this way. Wrap the list literal
in parentheses like this:
for $x (qw(a b c)) { ... }
This is being deprecated because the parentheses in for $i (1,2,3) {
... } are not part of expression syntax. They are part of the
statement syntax, with the for statement wanting literal parentheses.
The synthetic parentheses that a qw expression acquired were only
intended to be treated as part of expression syntax.
Looks like you can turn the warnings off with no warnings 'qw' but you'd be far better off fixing the code.
As a system administrator I find bad code unacceptable.
The best answer, of course, is to fix the offending perl scripts.
If your business won't let you fix the offending perl scripts, then don't upgrade Debian.
You have risks either way.

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.

Why does this base64 string comparison in Perl fail?

I am trying to compare an encode_base64('test') to the string variable containing the base64 string of 'test'. The problem is it never validates!
use MIMI::Base64 qw(encode_base64);
if (encode_base64("test") eq "dGVzdA==")
{
print "true";
}
Am I forgetting anything?
Here's a link to a Perlmonks page which says "Beware of the newline at the end of the encode_base64() encoded strings".
So the simple 'eq' may fail.
To suppress the newline, say encode_base64("test", "") instead.
When you do a string comparison and it fails unexpectedly, print the strings to see what is actually in them. I put brackets around the value to see any extra whitespace:
use MIME::Base64;
$b64 = encode_base64("test");
print "b64 is [$b64]\n";
if ($b64 eq "dGVzdA==") {
print "true";
}
This is a basic debugging technique using the best debugger ever invented. Get used to using it a lot. :)
Also, sometimes you need to read the documentation for things a couple time to catch the important parts. In this case, MIME::Base64 tells you that encode_base64 takes two arguments. The second argument is the line ending and defaults to a newline. If you don't want a newline on the end of the string you need to give it another line ending, such as the empty string:
encode_base64("test", "")
Here's an interesting tip: use Perl's wonderful and well-loved testing modules for debugging. Not only will that give you a head start on testing, but sometimes they'll make your debugging output a lot faster. For example:
#!/usr/bin/perl
use strict;
use warnings;
use Test::More 0.88;
BEGIN { use_ok 'MIME::Base64' => qw(encode_base64) }
is( encode_base64("test", "dGVzdA==", q{"test" encodes okay} );
done_testing;
Run that script, with perl or with prove, and it won't just tell you that it didn't match, it will say:
# Failed test '"test" encodes okay'
# at testbase64.pl line 6.
# got: 'gGVzdA==
# '
# expected: 'dGVzdA=='
and sharp-eyed readers will notice that the difference between the two is indeed the newline. :)

How can I eval environment variables in Perl?

I would like to evaluate an environment variable and set the result to a variable:
$x=eval($ENV{EDITOR});
print $x;
outputs:
/bin/vi
works fine.
If I set an environment variable QUOTE to \' and try the same thing:
$x=eval($ENV{QUOTE});
print $x;
outputs:
(nothing)
$# set to: "Can't find a string terminator anywhere before ..."
I do not wish to simply set $x=$ENV{QUOTE}; as the eval is also used to call a script and return its last value (very handy), so I would like to stick with the eval(); Note that all of the Environment variables eval'ed in this manner are set by me in a different place so I am not concerned with malicious access to the environment variables eval-ed in this way.
Suggestions?
Well, of course it does nothing.
If your ENV varaible contains text which is half code, but isn't and you give the resulting string to something that evaluates that code as Perl, of course it's not going to work.
You only have 3 options:
Programmatically process the string so it doesn't have invalid syntax in it
Manually make sure your ENV variables are not rubbish
Find a solution not involving eval but gives the right result.
You may as well complain that
$x = '
Is not valid code, because that's essentially what's occurring.
Samples of Fixing the value of 'QUOTE' to work
# Bad.
QUOTE="'" perl -wWe 'print eval $ENV{QUOTE}; print "$#"'
# Can't find string terminator "'" anywhere before EOF at (eval 1) line 1.
# Bad.
QUOTE="\'" perl -wWe 'print eval $ENV{QUOTE}; print "$#"'
# Can't find string terminator "'" anywhere before EOF at (eval 1) line 1.
# Bad.
QUOTE="\\'" perl -wWe 'print eval $ENV{QUOTE}; print "$#"'
# Can't find string terminator "'" anywhere before EOF at (eval 1) line 1.
# Good
QUOTE="'\''" perl -wWe 'print eval $ENV{QUOTE}; print "$#"'
# '
Why are you eval'ing in the first place? Should you just say
my $x = $ENV{QUOTE};
print "$x\n";
The eval is executing the string in $ENV{QUOTE} as if it were Perl code, which I certainly hope it isn't. That is why \ disappears. If you were to check the $# variable you would find an error message like
syntax error at (eval 1) line 2, at EOF
If you environment variables are going to contain code that Perl should be executing then you should look into the Safe module. It allows you to control what sort of code can execute in an eval so you don't accidentally wind up executing something like "use File::Find; find sub{unlink $File::Find::file}, '.'"
Evaluating an environment value is very dangerous, and would generate errors if running under taint mode.
# purposely broken
QUOTE='`rm system`'
$x=eval($ENV{QUOTE});
print $x;
Now just imagine if this script was running with root access, and was changed to actually delete the file system.
Kent's answer, while technically correct, misses the point. The solution is not to use eval better, but to not use eval at all!
The crux of this problem seems to be in understanding what eval STRING does (there is eval BLOCK which is completely different despite having the same name). It takes a string and runs it as Perl code. 99.99% this is unnecessary and dangerous and results in spaghetti code and you absolutely should not be using it so early in your Perl programming career. You have found the gun in your dad's sock drawer. Discovering that it can blow holes in things you are now trying to use it to hang a poster. It's better to forget it exists, your code will be so much better for it.
$x = eval($ENV{EDITOR}); does not do what you think it does. I don't even have to know what you think it does, that you even used it there means you don't know. I also know that you're running with warnings off because Perl would have screamed at you for that. Why? Let's assume that EDITOR is set to /bin/vi. The above is equivalent to $x = /bin/vi which isn't even valid Perl code.
$ EDITOR=/bin/vi perl -we '$x=eval($ENV{EDITOR}); print $x'
Bareword found where operator expected at (eval 1) line 1, near "/bin/vi"
(Missing operator before vi?)
Unquoted string "vi" may clash with future reserved word at (eval 1) line 2.
Use of uninitialized value $x in print at -e line 1.
I'm not sure how you got it to work in the first place. I suspect you left something out of your example. Maybe tweaking EDITOR until it worked?
You don't have to do anything magical to read an environment variable. Just $x = $ENV{EDITOR}. Done. $x is now /bin/vi as you wanted. It's just the same as $x = $y. Same thing with QUOTE.
$ QUOTE=\' perl -wle '$x=$ENV{QUOTE}; print $x'
'
Done.
Now, I suspect what you really want to do is run that editor and use that quote in some shell command. Am I right?
Well, you could double-escape the QUOTE's value, I guess, since you know that it's going to be evaled.
Maybe what you want is not Perl's eval but to evaluate the environment variable as the shell would. For this, you want to use backticks.
$x = `$ENV{QUOTE}`