strange thing in perl's eval function - perl

I get the following warning:
"Use of uninitialized value in concatenation (.) or string at C:\tools\test.pl line 17, DATA line 1."
But the next line of __DATA__ will be processed without any warning and get these:
test1b.txt:test test1c.txt:test :test
More strange thing is that when I add a line: print "$line:".$'."\n"; The warning disappeared.
Anybody have some clues?
#!/usr/bin/perl -w
use strict;
my $pattern='test';
my $output='$&';
while(<DATA>)
{
chomp;
my $line=$_;
chomp($line);
$line=~/$pattern/;
#print "$line:".$&."\n"; #why uncommenting this line make the following line pass without no warning.
my $result="$line:".eval($output)."\n";
print $result;
}
__DATA__
test1a.txt
test1b.txt
test1c.txt

Perl considers $&, $', and $` to be expensive, so it won't actually populate them in a program that doesn't use them. From the perlvar manpage:
The use of this variable [$&] anywhere in a program imposes a considerable
performance penalty on all regular expression matches. To avoid this
penalty, you can extract the same substring by using #-. Starting
with Perl 5.10, you can use the /p match flag and the ${^MATCH}
variable to do the same thing for particular match operations.
However, when you only use them inside a string that you pass to eval, Perl can't tell that you're using them, so it won't populate them, so they'll be undefined.

Related

How can I interpolate a variable from a command line argument into a string?

My program takes a command line argument which I would like to use to change the working directory of my Perl script.
use strict;
use warnings;
use Getopt::Std;
use Cwd 'chir';
my %opts=();
getopts('a:v:l:', \%opts);
my $application = $opts{a};
my $version = $opts{v};
my $location = $opts{l};
print "$application, $version, $location\n";
if($application eq 'abc') {
#print "you came here\n";
chdir "/viewstore/ccwww/dst_${application}_${version}/abc/${location}";
print $ENV{PWD};
print "you came here\n";
}
I've previously tried with chdir '/var/tmp/dst_$application/$version/$location';, but that did not work either.
The current version of the code gives this warning.
Global symbol "$application_" requires explicit package name at ./test.pl line 20. Execution of ./test.pl aborted due to compilation errors.
Line 20 is the one with chdir.
You are using single quotes '' in your chdir command. Single quotes do not do variable interpolation in Perl. That means that '/var/tmp/dst_$application/...' means /var/tmp/dst_$application/..., and not /var/tmp/dst_foo/....
You need to use double quotes "" to interpolate variables in strings.
chdir "/var/tmp/dst_$application/$version/$location";
This will create /var/tmp/dst_foo/... instead.
If you need to separate a variable from the rest of the string, use this notation.
print "${foo}bar";
This is different from "$foobar" because Perl thinks the whole $foobar is the variable name.

Perl eval command not working as expected

I have a sort of perl "terminal" (pastebin code) we'll call it that I've written, the idea behind writing it is I wanted to run perl code line by line, allowing me to run new commands on existing (large) data sets, without having to change a script and reload the data set and re-run my script.
(Mind you, I wrote this almost a year ago now, and it was mostly a learning experiment (with a dynamic function tablet), however now I have some use for it and discovered some issues which are preventing me from utilising it.)
As such, I eval user entered commands, however, they aren't behaving as expected and perhaps someone can shed some light on why this would be.
This is the 'important' bit, I have the command line data stored in #args, and the first element of that is stored in $prog. I check if there's an existing function (I allow users to create functions, and really abuse references to get an action table) if not I try and eval the command.
if(exists($actions{$prog})){
print "\n";
$actions{$prog}->(#args);
print "\n";
}else{
print "\nEVALing '$command'\n";
eval $command;
warn $# if $#;
print "\n";
}
As can be seen below, it works as expected for the assignment of scalars, but fails with the assignment of arrays and hashes.
user#host:~/$ perl term.pl
1358811935>$a = 0;
EVALing '$a = 0;'
1358811937>print $a;
EVALing 'print $a;'
0
1358811944>#b = qw(2 3);
EVALing '#b = qw(2 3);'
Global symbol "#b" requires explicit package name at (eval 5) line 1.
1358811945>print #b;
EVALing 'print #b;'
Global symbol "#b" requires explicit package name at (eval 6) line 1.
1358812008>my #b = qw(2 3);
EVALing 'my #b = qw(2 3);'
1358812008>print "#b";
EVALing 'print "#b";'
Possible unintended interpolation of #b in string at (eval 9) line 1.
Global symbol "#b" requires explicit package name at (eval 9) line 1.
1358812016>print join(',',#b);
EVALing 'print join(',',#b);'
Global symbol "#b" requires explicit package name at (eval 10) line 1.
1358812018>
Variables $a and $b are special, because they are used by sort. Therefore, strict does not complain if they are not declared. Using $x would trigger the same error as arrays and hashes.
For this kind of thing, you probably want to allow arbitrary package variables to be used by saying no strict 'vars';. Declaring a lexical (my) variable in the eval'd code will work, but will no longer be in scope for the next eval.
Alternatively, pre-declare a set of variables for the eval'd code to use (perhaps including a %misc hash).
A completely different approach is to each time through eval a concatenation of all the code entered so far (if printing output is a factor, redirecting output up until the most recent code entered).

Use of uninitialized value in concatenation (.) or string at mksmksmks.pl line 63

I have written some code, and I am not sure what the error is. I am getting the error:
Use of uninitialized value in concatenation (.) or string at mksmksmks.pl line 63
My code is as follows:
for(my $j = 0; $j < $num2; $j++) {
print {$out} "$destination[$j]|$IP_one_1[$j]|$IP_one_2[$j]|$reached[$j]|$IP_two_1[$j]|$IP_two_2[$j]\n";`
}
What it means is that one of the elements of either #destination, #IP_one_1, #IP_one_2, or #reached has not been defined (has not been assigned a value), or has been assigned a value of undef. You either need to detect (and prevent) undefined values at the source, or expect and deal with them later on. Since you have warnings enabled (which is a good thing), Perl is reminding you that your code is trying to concatenate a string where one of the values being concatenated is undefined.
Consider the following example:
perl -wE 'my #x = (); $x[0] = "Hello "; $x[2] = "world!"; say "#x"'
In this example, $x[0] has a value, and $x[2] has a value, but $x[1] does not. When we interpolate #x into a double-quoted construct, it is expanded as [element 0 (Hello )]<space>[element 1 (undef)]<space>[element 2 (world!)]. The undef elements interpolates as an empty string, and spews a warning. And of course by default array interpolation injects a space character between each element. So in the above example we see Hello <interpolation-space>(undef upgrades to empty string here)<interpolation-space>world!
An example of where you might investigate is one or more of the arrays is of a different total size than the others. For example, if #IP_one_2 has fewer elements than the others, or if $num2 is a value greater than the number of elements in any of the arrays.
Place the following near the top of your script and run it again:
use diagnostics;
When I run the following one-liner under warnings and diagnostics:
$ perl -wMdiagnostics -e '$a=$a; print "$a\n"'
I get the following output, and you will get something similar if you add use diagnostics;... a very helpful tool when you're first learning Perl's warnings.
Use of uninitialized value $a in concatenation (.) or string at -e
line 1 (#1)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you
the name of the variable (if any) that was undefined. In some cases
it cannot do this, so it also tells you what operation you used the
undefined value in. Note, however, that perl optimizes your program
anid the operation displayed in the warning may not necessarily appear
literally in your program. For example, "that $foo" is usually
optimized into "that " . $foo, and the warning will refer to the
concatenation (.) operator, even though there is no . in
your program.
Maybe my example will be useful to someone. Suppose variable $x is initialized from a database. It may contain an undefined value, and this is normal. We need to display its value on the console. As responsible programmers, we decided to use "use warnings FATAL => "all";". In this case, the script will fail.
perl -e 'use strict; use warnings FATAL => "all"; my $x; print("x=$x\n"); print("DONE\n");'
Returns:
Use of uninitialized value $x in concatenation (.) or string at -e line 1.
In this case, you can use
if(defined($x)){...}else{...}
But this is not pretty if just want to print a value.
perl -e 'use strict; use warnings FATAL => "all"; my $x; print("x=".($x//"null")."\n"); print("DONE\n");'
Prints:
x=null
DONE
Because the expression $x//"null" checks whether what comes before // is defined and if it is not defined returns what comes after //.
If you use eq "" it won't give any warning message.
But if you use eq " " (here you can see a space), then it will give this warning message:
Use of uninitialized value in concatenation (.) or string ....

How do I print on a single line all content between certain start- and stop-lines?

while(<FILE>)
{
chomp $_;
$line[$i]=$_;
++$i;
}
for($j=0;$j<$i;++$j)
{
if($line[$j]=~/Syn_Name/)
{
do
{
print OUT $line[$j],"\n";
++$j;
}
until($line[$j]=~/^\s*$/)
}
}
This is my code I am trying to print data between Syn_Name and a blank line.
My code extracts the chunk that I need.
But the data between the chunk is printed line by line. I want the data for each chunk to get printed on a single line.
Simplification of your code. Using the flip-flop operator to control the print. Note that printing the final line will not add a newline (unless the line contained more than one newline). At best, it prints the empty string. At worst, it prints whitespace.
You do not need a transition array for the lines, you can use a while loop. In case you want to store the lines anyway, I added a commented line with how that is best done.
#chomp(my #line = <FILE>);
while (<FILE>) {
chomp;
if(/Syn_Name/ .. /^\s*$/) {
print OUT;
print "\n" if /^\s*$/;
}
}
Contents
Idiomatic Perl
Make errors easier to fix
Warnings about common programming errors
Don't execute unless variable names are consistent
Developing this habit will save you lots of time
Perl's range operator
Working demos
Print chomped lines immediately
Join lines with spaces
One more edge case
Idiomatic Perl
You seem to have a background with the C family of languages. This is fine because it gets the job done, but you can let Perl handle the machinery for you, namely
chomp defaults to $_ (also true with many other Perl operators)
push adds an element to the end of an array
to simplify your first loop:
while (<FILE>)
{
chomp;
push #line, $_;
}
Now you don't have update $i to keep track of how many lines you've already added to the array.
On the second loop, instead of using a C-style for loop, use a foreach loop:
The foreach loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn …
The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity. (Or because the Bourne shell is more familiar to you than csh, so writing for comes more naturally.) If VAR is omitted, $_ is set to each value.
This way, Perl handles the bookkeeping for you.
for (#line)
{
# $_ is the current element of #line
...
}
Make errors easier to fix
Sometimes Perl can be too accommodating. Say in the second loop you made an easy typographical error:
for (#lines)
Running your program now produces no output at all, even if the input contains Syn_Name chunks.
A human can look at the code and see that you probably intended to process the array you just created and pluralized the name of the array by mistake. Perl, being eager to help, creates a new empty #lines array, which leaves your foreach loop with nothing to do.
You may delete the spurious s at the end of the array's name but still have a program produces no output! For example, you may have an unhandled combination of inputs that doesn't open the OUT filehandle.
Perl has a couple of easy ways to spare you these (and more!) kinds of frustration from dealing with silent failures.
Warnings about common programming errors
You can turn on an enormous list of warnings that help diagnose common programming problems. With my imagined buggy version of your code, Perl could have told you
Name "main::lines" used only once: possible typo at ./synname line 16.
and after fixing the typo in the array name
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
Right away, you see valuable information that may be difficult or at least tedious to spot unaided:
variable names are inconsistent, and
the program is trying to produce output but needs a little more plumbing.
Don't execute unless variable names are consistent
Notice that even with the potential problems above, Perl tried to execute anyway. With some classes of problems such as the variable-naming inconsistency, you may prefer that Perl not execute your program but stop and make you fix it first. You can tell Perl to be strict about variables:
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.
The tradeoff is you have to be explicit about which variables you intend to be part of your program instead of allowing them to conveniently spring to life upon first use. Before the first loop, you would declare
my #line;
to express your intent. Then with the bug of a mistakenly pluralized array name, Perl fails with
Global symbol "#lines" requires explicit package name at ./synname line 16.
Execution of ./synname aborted due to compilation errors.
and you know exactly which line contains the error.
Developing this habit will save you lots of time
I begin almost every non-trivial Perl program I write with
#! /usr/bin/env perl
use strict;
use warnings;
The first is the shebang line, an ordinary comment as far as Perl is concerned. The use lines enable the strict pragma and the warnings pragma.
Not wanting to be a strict-zombie, as Mark Dominus chided, I'll point out that use strict; as above with no option makes Perl strict in dealing with three error-prone areas:
strict vars, as described above;
strict refs, disallows use of symbolic references; and
strict subs, requires the programmer to be more careful in referring to subroutines.
This is a highly useful default. See the strict pragma's documentation for more details.
Perl's range operator
The perlop documentation describes .., Perl's range operator, that can help you greatly simplify the logic in your second loop:
In scalar context, .. returns a boolean value. The operator is bistable, like a flip-flop, and emulates the line-range (comma) operator of sed, awk, and various editors. Each .. operator maintains its own boolean state, even across calls to a subroutine that contains it. It is false as long as its left operand is false. Once the left operand is true, the range operator stays true until the right operand is true, AFTER which the range operator becomes false again. It doesn't become false till the next time the range operator is evaluated.
In your question, you wrote that you want “data between Syn_Name and a blank line,” which in Perl is spelled
/Syn_Name/ .. /^\s*$/
In your case, you also want to do something special at the end of the range, and .. provides for that case too, ibid.
The final sequence number in a range has the string "E0" appended to it, which doesn't affect its numeric value, but gives you something to search for if you want to exclude the endpoint.
Assigning the value returned from .. (which I usually do to a scalar named $inside or $is_inside) allows you to check whether you're at the end, e.g.,
my $is_inside = /Syn_Name/ .. /^\s*$/;
if ($is_inside =~ /E0$/) {
...
}
Writing it this way also avoids duplicating the code for your terminating condition (the right-hand operand of ..). This way if you need to change the logic, you change it in only one place. When you have to remember, you'll forget sometimes and create bugs.
Working demos
See below for code you can copy-and-paste to get working programs. For demo purposes, they read input from the built-in DATA filehandle and write output to STDOUT. Writing it this way means you can transfer my code into yours with little or no modification.
Print chomped lines immediately
As defined in your question, there's no need for one loop to collect the lines in a temporary array and then another loop to process the array. Consider the following code
#! /usr/bin/env perl
use strict;
use warnings;
# for demo only
*FILE = *DATA;
*OUT = *STDOUT;
while (<FILE>)
{
chomp;
if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
my $is_last = $is_inside =~ /E0$/;
print OUT $_, $is_last ? "\n" : ();
}
}
__DATA__
ERROR IF PRESENT IN OUTPUT!
Syn_Name
foo
bar
baz
ERROR IF PRESENT IN OUTPUT!
whose output is
Syn_Namefoobarbaz
We always print the current line, stored in $_. When we're at the end of the range, that is, when $is_last is true, we also print a newline. When $is_last is false, the empty list in the other branch of the ternary operator is the result—meaning we print $_ only, no newline.
Join lines with spaces
You didn't show us an example input, so I wonder whether you really want to butt the lines together rather than joining them with spaces. If you want the latter behavior, then the program becomes
#! /usr/bin/env perl
use strict;
use warnings;
# for demo only
*FILE = *DATA;
*OUT = *STDOUT;
my #lines;
while (<FILE>)
{
chomp;
if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
push #lines, $_;
if ($is_inside =~ /E0$/) {
print OUT join(" ", #lines), "\n";
#lines = ();
}
}
}
__DATA__
ERROR IF PRESENT IN OUTPUT!
Syn_Name
foo
bar
baz
ERROR IF PRESENT IN OUTPUT!
This code accumulates in #lines only those lines within a Syn_Name chunk, prints the chunk, and clears out #lines when we see the terminator. The output is now
Syn_Name foo bar baz
One more edge case
Finally, what happens if we see Syn_Name at the end of the file but without a terminating blank line? That may be impossible with your data, but in case you need to handle it, you'll want to use Perl's eof operator.
eof FILEHANDLE
eof
Returns 1 if the next read on FILEHANDLE will return end of file or if FILEHANDLE is not open … An eof without an argument uses the last file read.
So we terminate on either a blank line or end of file.
#! /usr/bin/env perl
use strict;
use warnings;
# for demo only
*FILE = *DATA;
*OUT = *STDOUT;
my #lines;
while (<FILE>)
{
s/\s+$//;
#if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
if (my $is_inside = /Syn_Name/ .. /^\s*$/ || eof) {
push #lines, $_;
if ($is_inside =~ /E0$/) {
print OUT join(" ", #lines), "\n";
#lines = ();
}
}
}
__DATA__
ERROR IF PRESENT IN OUTPUT!
Syn_Name
foo
bar
YOU CANT SEE ME!
Syn_Name
quux
potrzebie
Output:
Syn_Name foo bar
Syn_Name quux potrzebie
Here instead of chomp, the code removes any trailing invisible whitespace at the ends of lines. This will make sure spacing between joined lines is uniform even if the input is a little sloppy.
Without the eof check, the program does not print the latter line, which you can see by commenting out the active conditional and uncommenting the other.
Another simplified version:
foreach (grep {chomp; /Syn_Name/ .. /^\s*$/ } <FILE>) {
print OUT;
print OUT "\n" if /^\s*$/;
}

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. :)