perl eval without block - perl

I am learning perl eval. I understand how to use eval BLOCK, but I have came across the code below. What is the code below doing?
while(<>) {
eval;
warn $# if $#;
}

while(<>) {
This reads input, and places it in the variable $_. The input used by <> is first #ARGV (if you called your script with arguments), then STDIN (standard input).
Information on the diamond operator here.
eval;
This evaluates the line that was read, since not specifying what to evaluate looks at $_.
warn $# if $#;
This line will display the warnings that appear in $#, if there are any.

Perl's eval() builtin can take either a BLOCK or an EXPR. If it is given an EXPR that EXPR will be evaluated as a string which contains Perl code to be executed.
For example:
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
eval { say "Hello, Block World!"; };
eval 'say "Hello, String World!";';
This code executes both say()s as you would expect.
$ ./evals.pl
Hello, Block World!
Hello, String World!
In general the string version of eval() is considered dangerous, especially if you allow interpolation into that string based on variables that are coming from outside your control. For exmaple:
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
my $name = $ARGV[0] // 'World';
eval "say 'Hello, $name';";
This code is safe if called as so:
$ ./evals.pl Kaoru
Hello, Alex
$ ./evals.pl
Hello, World
But would be very dangerous if the user called it as:
$ ./evals.pl "Kaoru'; system 'rm -rf /"
On the other hand, in string eval()'s favour, it can be very useful as the opposite of Data::Dumper::Dumper() for turning dumped Perl code back into Perl-internal data structures. For example:
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my $hashref = { a => 1, b => 2, c => 3 };
print Dumper $hashref;
my $VAR1;
my $hashref_copy = eval Dumper $hashref;
say $hashref_copy->{b};
Which, as you would expect, outputs:
$ ./evals.pl
$VAR1 = {
'c' => 3,
'b' => 2,
'a' => 1
};
2
See perldoc -f eval or http://perldoc.perl.org/functions/eval.html for more details.
As of Perl 5.16.3, there is also an evalbytes() which treats the string as a byte string rather than a character string. See perldoc -f perlunicode or http://perldoc.perl.org/perlunicode.html for more details on the difference between character strings and byte strings.
The code which you asked about explicitly:
while(<>) {
eval;
warn $# if $#;
}
Is reading in each line of either STDIN or the files specified in #ARGV, and evaluating each line of input as a line of Perl code. If that Perl code fails to compile, or throws an exception via die(), the error is warned to STDERR. perldoc -f eval has the full details of how and why eval() might set $#.
As an example of the code being called:
$ echo 'print "foo\\n";' | ./evals.pl
foo
$ echo 'print 1 + 1, "\\n";' | ./evals.pl
2
$ echo 'dfsdfsdaf' | ./evals.pl
Bareword "dfsdfsdaf" not allowed while "strict subs" in use at (eval 1) line 1, <> line 1.
$ echo 'die "dead";' | ./evals.pl
dead at (eval 1) line 1, <> line 1.

Related

Perl eval scope

According to perldoc, String Eval should be performed in the current scope. But the following simple test seems to contradict this.
We need the following two simple files to set up the test. Please put them under the same folder.
test_eval_scope.pm
package test_eval_scope;
use strict;
use warnings;
my %h = (a=>'b');
sub f1 {
eval 'print %h, "\n"';
# print %h, "\n"; # this would work
# my $dummy = \%h; # adding this would also work
}
1
test_eval_scope.pl
#!/usr/bin/perl
use File::Basename;
use lib dirname (__FILE__);
use test_eval_scope;
test_eval_scope::f1();
When I run the program, I got the following error
$ test_eval_scope.pl
Variable "%h" is not available at (eval 1) line 1.
My question is why the variable %h is out of scope.
I have done some modification, and found the following:
If I run without eval(), as in the above comment, it will work.
meaning that %h should be in the scope.
If I just add a seemingly useless mentioning in the code, as in the above
comment, eval() will work too.
If I combine pl and pm file into one file, eval() will work too.
If I declare %h with 'our' instead of 'my', eval() will work too.
I encountered this question when I was writing a big program which parsed user-provided code during run time. I don't need solutions as I have plenty workarounds above. But I cannot explain why the above code doesn't work. This affects my perl pride.
My perl version is v5.26.1 on linux.
Thank you for your help!
Subs only capture variables they use. Since f1 doesn't use %h, it doesn't capture it, and %h becomes inaccessible to f1 after it goes out of scope when the module finishes executing.
Any reference to the var, including one that's optimized away, causes the sub to capture the variable. As such, the following does work:
sub f1 {
%h if 0;
eval 'print %h, "\n"';
}
Demo:
$ perl -M5.010 -we'
{
my $x = "x";
sub f { eval q{$x} }
sub g { $x if 0; eval q{$x} }
}
say "f: ", f();
say "g: ", g();
'
Variable "$x" is not available at (eval 1) line 1.
Use of uninitialized value in say at -e line 8.
f:
g: x

why does perl while (<>) fail to count or print the first line

I want to count the lines in a file and print a string which depends on the line number. But my while loop misses the first line. I believe the while (<>) construct is necessary to increment the $n variable; anyway, is not this construct pretty standard in perl?
How do I get the while loop to print the first line? Or should I not be using while?
> printf '%s\n%s\n' dog cat
dog
cat
> printf '%s\n%s\n' dog cat | perl -n -e 'use strict; use warnings; print; '
dog
cat
> printf '%s\n%s\n' dog cat | perl -n -e 'use strict; use warnings; while (<>) { print; } '
cat
>
> printf '%s\n%s\n' dog cat | perl -n -e 'use strict; use warnings; my $n=0; while (<>) { $n++; print "$n:"; print; } '
1:cat
The man perlrun shows:
-n causes Perl to assume the following loop around your program, which makes it iterate over filename
arguments somewhat like sed -n or awk:
LINE:
while (<>) {
... # your program goes here
}
Note that the lines are not printed by default. See "-p" to have lines printed. If a file named by an
argument cannot be opened for some reason, Perl warns you about it and moves on to the next file.
Also note that "<>" passes command line arguments to "open" in perlfunc, which doesn't necessarily
interpret them as file names. See perlop for possible security implications.
...
...
"BEGIN" and "END" blocks may be used to capture control before or after the implicit program loop, just as in awk.
So, in fact you running this script
LINE:
while (<>) {
# your progrem start
use strict;
use warnings;
my $n=0;
while (<>) {
$n++;
print "$n:";
print;
}
# end
}
Solution, just remove the -n.
printf '%s\n%s\n' dog cat | perl -e 'use strict; use warnings; my $n=0; while (<>) { $n++; print "$n:"; print; }'
Will print:
1:dog
2:cat
or
printf '%s\n%s\n' dog cat | perl -ne 'print ++$n, ":$_"'
with the same result
or
printf '%s\n%s\n' dog cat | perl -pe '++$n;s/^/$n:/'
but the ikegami's solution
printf "one\ntwo\n" | perl -ne 'print "$.:$_"'
is the BEST
There's a way to figure out what your one-liner is actually doing. The B::Deparse module has a way to show you how perl interpreted your source code. It's actually from the O (capital letter O, not zero) namespace that you can load with -M (ikegami explains this on Perlmonks):
$ perl -MO=Deparse -ne 'while(<>){print}' foo bar
LINE: while (defined($_ = readline ARGV)) {
while (defined($_ = readline ARGV)) {
print $_;
}
-e syntax OK
Heh, googling for the module link shows I wrote about this for The Effective Perler. Same example. I guess I'm not that original.
If you can't change the command line, perhaps because it's in the middle of a big script or something, you can set options in PERL5OPT. Then those options last for just the session. I hate changing the original scripts because it seems that no matter how careful I am, I mess up something (how many times has my brain told me "hey dummy, you know what a git branch is, so you should have used that first"):
$ export PERL5OPT='-MO=Deparse'

How to grep square brackets in perl

I am trying to grep [0](including square brackets) in a file using perl, I tried following code
my #output = `grep \"\[0\]\" log `;
But instead of returning [0], it is giving output where it matches 0
Your problem is that you need to escape the [ and ] twice, as [ ... ] has a special meaning in regexes (it defines a character class).
#!/usr/bin/perl
use strict;
use warnings;
my #output = `grep "\\[0\\]" log `;
print for #output;
But you really don't need to use the external grep command. Perl is great at text processing.
#!/usr/bin/perl
use strict;
use warnings;
while (<>) {
print if /\[0\]/;
}
My solution reads from any file whose name is given as an argument to the program (or from STDIN).

cygwin perl subroutine executing out of order

I wrote this simple program with a subroutine.
It acts strangely when I put it in a sub routine.
#!/usr/bin/perl -w
use strict ;
sub get_name {
print "what is your name ?\n" ;
my $name = <STDIN> ;
chomp($name) ;
print "Hello $name\n";
}
get_name ;
This is the result
$ ./name_print
casper
what is your name ?
Hello casper
It never prints the what is your name string.
It waits for me to enter in some standard input , and then prints the "what is your name string". It executes out of order.
Very strange.
This type of behaviour can be created if perl is confused about what type of buffering it should do to the IO. Try adding the following line.
$|++;
Just after your 'use strict;' to force perl to flush the buffers after every print statement.

Perl confusion over grep {//} and eval {grep //} syntax

Kindly shed some light on these two ways of grep'ping in Perl as how they differ from each other
eval {grep /pattern/, ....};
and the normal one,
grep {/pattern/} ....
First of all, there are 2 independent differences between your alternatives, and they have different purposes. Wrapping the grep in eval allows you to catch errors that are normally fatal (like a syntax error in the regular expression). Putting a block after the grep keyword lets you use a matching rule that is more complex than a single expression.
Here are the 4 combinations that can be made out of your 2 examples:
#y = grep /pattern/, #x; # grep EXPR, no eval
#y = grep { /pattern/ } #x; # grep BLOCK, no eval
eval { #y = grep /pattern/, #x }; # grep EXPR inside eval BLOCK
eval { #y = grep { /pattern/ } #x }; # grep BLOCK inside eval BLOCK
Now we can look in more detail at 2 separate questions: what do you gain from the eval, and what do you gain from using the grep BLOCK syntax? In the simple cases shown above, you gain nothing from either one.
When you want to do a grep where the matching condition is more complicated than a simple regexp, grep BLOCK gives you more flexibility in how you express the condition. You can put multiple statements in the block and use temporary variables. For example this grep within a grep:
# Note: not the most efficient method for finding an intersection of arrays.
my #a = qw/A E I O U/;
my #b = qw/A B D O P Q R/;
my #intersection = grep { my $x = $_; grep { $_ eq $x } #b } #a;
print "#intersection\n";
In the above example, we needed a temporary $x to hold the value being tested by the outer grep so it could be compared to $_ in the inner grep. The inner grep could have been written without a BLOCK as grep $_ eq $x, #b but I think having using the same syntax for both looks better.
The eval block would be useful if you were looking for matches of a regexp that is determined at runtime, and you don't want your program to abort when the regexp is invalid. For example:
#x = qw/foo bar baz quux xyzzy/;
do {
print STDERR 'Enter pattern: ';
$pat = <STDIN>;
chomp $pat;
eval {
#y = grep /$pat/, #x;
};
} while($#);
print "result: #y\n";
We ask the user for a pattern and print the list of matches from #x. If the pattern is not a valid regexp, the eval catches the error and puts it into $#, and the program keeps running (The "Invalid" message is printed and the loop continues so the user can try again.) When a valid regexp is entered, there is no error so $# is false the "result" line is printed. Sample run:
Enter pattern: z$
result: baz
Enter pattern: ^(?!....)
result: foo bar baz
Enter pattern: ([^z])\1
result: foo quux
Enter pattern: [xyz
Invalid pattern
Enter pattern: [xyz]
result: baz quux xyzzy
Enter pattern: ^C
Note that eval doesn't catch syntax errors in a fixed regexp. Those are compiled when the script is compiled, so if you have a simple script like
perl -ne 'print if eval { /[xyz/ } or eval { /^ba/ }'
it fails immediately. The evals don't help. Compare to
perl -ne '$x = "[xyz"; $y = "^ba"; print if eval { /$x/ } or eval { /$y/ }'
which is the same thing but with regexps built from variables - this one runs and prints matches for /^ba/. The first eval always returns false (and sets $# which doesn't matter if you don't look at it).