Why doesn't die $template->error() show a line number? - perl

In the following short program:
use Template;
my $template = Template->new (INCLUDE_PATH => ".");
$template->process ("non-existent-file")
or die $template->error ();
why doesn't die produce a line number and newline? My output looks like this:
~ 502 $ perl template.pl
file error - non-existent-file: not found ~ 503 $

Template is returning an error object of type Template::Exception. The object has overloaded stringification which applies when the value is printed, but when die looks at the value, it sees a reference and doesn't append the line number and newline. Force the value into a string earlier to fix the problem:
use Template;
my $template = Template->new (INCLUDE_PATH => ".");
$template->process ("non-existent-file")
or die '' . $template->error ();
prints
file error - non-existent-file: not found at scratchpad.pl line 25.

While #Eric's answer does solve the OPs issue, I suggest appending a space instead of pre-pending an empty string.
The reason is that if there are problems in the template, the error will be reported as coming from the template text instead of the line number in the perl file (which is what I want). See this short example:
use Template;
my $template = Template->new();
# Clearly a division by zero bug
$template->process(\"[% 1 / 0 %]")
or die $template->error();
This results in:
undef error - Illegal division by zero at input text line 1.
Which isn't very helpful. I want the perl file location. Instead I suggest:
my $template = Template->new();
$template->process(\"[% 1 / 0 %]")
or die $template->error() . ' ';
which produces:
undef error - Illegal division by zero at input text line 1.
at test.pl line 11.
This way I get the line number in the perl file too. It does look a little ugly, though. (You can stop reading now, if you like...)
An even more correct way would be:
use Template;
my $template = Template->new();
$template->process(\"[% 1 / 0 %]")
or do {
my $error = $template->error . '';
chomp $error;
die $error;
};
which produces this output:
undef error - Illegal division by zero at input text line 1. at t2.pl line 15.
But it is just so verbose and has a weird . in there. I've actually ended up creating:
sub templateError {
my ($template) = #_;
my $string = $template->error->as_string;
chomp $string;
$string =~ s/(line \d+)\.$/$1/;
return $string;
}
...
use Template;
my $template = Template->new ();
$template->process (\"[% 1 / 0 %]")
or die templateError($template);
So that I get this:
undef error - Illegal division by zero at input text line 1 at test.pl line 30.
as well as this for the OPs example:
file error - non-existent-file: not found at test.pl line 31.

Related

Unable to read the input file content that containing % sign

I am having simple open file input as showing below, which I have no problem reading the content from the input file, but not entirely readable.
open(IN,"<$modelRoot/Local_$project.pm") || die "ERROR\: $!";
while(<IN>)
{
$temp = $_;
chomp($temp);
printf "$temp\n";
}
The content that I printed out looks fine until the point that is a % sign.
This is the original input file content
my %LocalToolData = (
This is the content that I print out, it gives a warning too
Use of uninitialized value in printf at rfinteg_v4.pl line 846, <IN> line 24.
Use of uninitialized value in printf at rfinteg_v4.pl line 847, <IN> line 24.
my 0calToolData = (
Question: How do I read % sign from the input file and at the same time avoid having the warning?
printf does formatting using %, it's a reserved character. The first argument to printf is a template string, not any string.
You should be using print unless you want to make use of that feature.
You should not use printf with any content containing % unless you have corresponding placeholder values for that.
It even says in the documentation:
Don't fall into the trap of using a printf when a simple print would do. The print is more efficient and less error prone.

Use of uninitialized value $e2 in string eq & find: warning:

Hopefully you can help a scientist to decipher whats wrong with the code I'm trying to run to clean up some NGS results. The Perl file itself comes from https://github.com/mtokuyama/ERVmap, though I am posting the code below for reference. The other Perl files in the package work just fine and, while I have built a passing ability to use the linux terminal, Perl is a little beyond me.
The linux terminal I'm using is currently running: Ubuntu 16.04.6 LTS
This is the Perl code I'm trying to run using the following command line on linux as instructed by their GitHub page:
perl clean_htseq.pl ./ c c2 __
#!/usr/bin/env perl
#$Id: run_clean_htseq.pl,v 1.2 2015/03/02 17:24:35 yk336 Exp $
#
# create pbs file
#
use warnings;
use strict;
use File::Basename;
use POSIX;
my $dir = shift;
my $e1 = shift;
my $e2 = shift;
my $stop = shift;
die "$e1 eq $e2" if ($e1 eq $e2);
my $find = "find $dir -name \"*${e1}\"";
my $out = `$find`;
my #files = split(/\n/, $out);
for my $f (#files) {
my $o = $f;
$o =~ s/${e1}$/$e2/;
my $cmd = "./clean_htseq.pl $stop $f > $o";
print "$cmd\n";
system($cmd);
}
The first error that I had was that the _clean_htseq.pl_ wasn't found (line 30, already altered to solution) which i solved by adding the ./ in front of it and giving the software permission to use the script file.
My current issue with the code/command line is the following error:
Use of uninitialized value $e2 in string eq at ./clean_htseq.pl line 18.
find: warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '-name ‘*./SRR7251667.c’' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ ‘*./SRR7251667.c’'.
This has been tracked down to the "__" at the end of the command line, while i'm sure this is supposed to mean something to the script I removed it and resulted in the following error:
Use of uninitialized value $stop in concatenation (.) or string at clean_htseq.pl line 30.
./clean_htseq.pl ./SRR7251667.c > ./SRR7251667.c2
Use of uninitialized value $e1 in string eq at ./clean_htseq.pl line 18.
Use of uninitialized value $e2 in string eq at ./clean_htseq.pl line 18.
Use of uninitialized value $e1 in concatenation (.) or string at ./clean_htseq.pl line 18.
Use of uninitialized value $e2 in concatenation (.) or string at ./clean_htseq.pl line 18.
eq at ./clean_htseq.pl line 18.
An error occurs too when I remove the "." from "./" but it comes back with an error about not finding the _clean_htseq.pl_ file which is in the working directory.
Your problem seems to be here:
my $dir = shift;
my $e1 = shift;
my $e2 = shift;
my $stop = shift;
Outside of a subroutine, shift works on #ARGV—the array that holds the command line arguments. You shift four times, so you need four arguments:
perl clean_htseq.pl ./ c c2 __
You only seem to give it two, and $stop has no value (so you are giving it less than two):
./clean_htseq.pl $stop $f
You can't just remove arguments and hope things still work out. Likely you're going to have to look at the source to see what those things mean (which should motivate you as a scientist to use good variable names and document code—Best Practices for Scientific Computing).
A first step may be to set defaults. The defined-or operator does well here:
use v5.10;
my $dir = shift // 'default_dir';
my $e1 = shift // 'default_value';
my $e2 = shift // 'default_value';
my $stop = shift // 'default_value';
Or, you could just give up if there aren't enough arguments. An array in scalar context gives you the number of elements in the array (although it doesn't guarantee anything about their values):
die "Need four arguments!\n" unless #ARGV == 4;
There are various other improvements which would help this script, some of which I go through in the "Secure Programming Techniques" chapter in Mastering Perl. Taking unchecked user input and passing it to another program is generally not a good idea.

How to find number of numerical data for each and every line in a file

Please help me to count the numerical data in each line of a file,
and also to find the line length. The code has to written in Perl.
For example if I have a line such as:
INPUT:I was born on 24th october,1994.
Output:2
You could do something like this:
perl -ne 'BEGIN{my $x} $x += () = /[0-9]+/g; END{print($x . "\n")}' file
-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
}
-e: may be used to enter one line of program;
() will make /[0-9]+/g be evaluated in list context (i.e. () = /[0-9]+/g will return an array containing the sequences of one or more digits found in the default input), while $x += will make the result be evaluated again in scalar context (i.e. $x += () = /[0-9]+/g will add the number of sequences of one or more digits found in the default input to $x); END{print($x . "\n") will print $x after the whole file has been processed.
% cat file
string 123 string 1 string string string
456 string
% perl -ne 'BEGIN{my $x} $x += () = /[0-9]+/g; END{print($x . "\n")}' file
3
%
I'd do something like this
#!/usr/bin/perl
use warnings;
use strict;
my $file = 'num.txt';
open my $fh, '<', $file or die "Failed to open $file: $!\n";
while (my $line = <$fh>){
chomp $line;
my #num = $line =~ /([0-9.]+)/g;
print "On this line --- " .scalar(#num) . "\n";
}
close ($fh);
The input file I tested --
This should say 1
Line 2 should say 2
I want this line to say 5 so I have added 4 other numbers like 0.02 -1 and 5.23
The output as tested ----
On this line --- 1
On this line --- 2
On this line --- 5
Using the regex match ([0-9.]+) will match ANY number and include any decimals (I guess really you could use just ([0-9]+) since you are only counting them and not using the actually number represented.)
Hope it helps.

perl error: Use of uninitialized value $_ in concatenation (.) or string

I get the following error:
Use of uninitialized value $_ in concatenation (.) or string at checkfornewfiles.pl line 34.
when attempting to run the following code :
#!/usr/bin/perl -w
#Author: mimo
#Date 3/2015
#Purpose: monitor directory for new files...
AscertainStatus();
######### start of subroutine ########
sub AscertainStatus {
my $DIR= "test2";
####### open handler #############
opendir (HAN1, "$DIR") || die "Problem: $!";
########## assign theoutput of HAN1 to array1 ##########
my #array1= readdir(HAN1);
######## adding some logic #########
if ("$#array1" > 1) { #### if files exists (more than 1) in the directory #######
for (my $i=0; $i<2; $i++) {shift #array1;} ####### for i in position 0 (which is the . position) loop twice and add one (the position ..) get rid of them #######
MailNewFiles(#array1);
} else { print "No New Files\n";}
}
sub MailNewFiles {
$mail= "sendmail";
open ($mail, "| /usr/lib/sendmail -oi -t" ) ||die "errors with sendmail $!"; # open handler and pipe it to sendmail
print $mail <<"EOF"; #print till the end of fiEOF
From: "user";
To: "root";
Subject: "New Files Found";
foreach (#_) {print $mail "new file found:\n $_\n";}
EOF
close($mail);
}
#End
I am new to perl and I don't know what's going wrong. Can anyone help me ?
A few suggestions:
Perl isn't C. Your main program loop shouldn't be a declared subroutine which you then execute. Eliminate the AscertainStatus subroutine.
Always, always use strict; and use warnings;.
Indent correctly. It makes it much easier for people to read your code and help analyze what you did wrong.
Use a more modern Perl coding style. Perl is an old language, and over the years new coding style and techniques have been developed to help you eliminate basic errors and help others read your code.
Don't use system commands when there are Perl modules that can do this for you in a more standard way, and probably do better error checking. Perl comes with the Net::SMTP that handles mail communication for you. Use that.
The error Use of uninitialized value $_ in concatenation (.) or string is exactly what it says. You are attempting to use a value of a variable that hasn't been set. In this case, the variable is the #_ variable in your foreach statement. Your foreach isn't a true foreach, but part of your print statement since your EOF is after your for statement. This looks like an error.
Also, what is the value of #_? This variable contains a list of values that have been passed to your subroutine. If none are passed, it will be undefined. Even if #_ is undefined, foreach (undef) will simply skip the loop. However, since foreach (#_) { is a string to print, your Perl program will crash without #_ being defined.
If you remove the -w from #!/usr/bin/perl, your program will actually "work" (Note the quotes), and you'll see that your foreach will literally print.
I do not recommend you not to use warnings which is what -w does. In fact, I recommend you use warnings; rather than -w. However, in this case, it might help you see your error.
You have EOF after the line with foreach. It contains $_ which is interpolated here but $_ is not initialized yet because it is not in foreach loop. It is not code but just text. Move EOF before foreach.
But probably you would like
sub MailNewFiles {
$mail= "sendmail";
open ($mail, "| /usr/lib/sendmail -oi -t" ) ||die "errors with sendmail $!"; # open handler and pipe it to sendmail
local $"="\n"; # " make syntax highlight happy
print $mail <<"EOF"; #print till the end of fiEOF
From: "user";
To: "root";
Subject: "New Files Found";
New files found:
#_
EOF
close($mail);
}
See perlvar for more informations about $".
The message
Use of uninitialized value $xxx in ...
is very straightforward. When you encounter it, it means that you are using a variable ($xxx) in any way, but that the variable has not ever been initialized.
Sometimes, adding an initialization command at the start of you code is enough :
my $str = '';
my $num = 0;
Sometimes, your algorithm is wrong, or you just mistyped your variable, like in :
my $foo = 'foo';
my $bar = $ffo . 'bar'; # << There is a warning on this line
# << because you made a mistake on $foo ($ffo)

Use of uninitialized value in concatenation (.) or string

I have just beginner in perl and write small piece of code.After Adding \n, I am getting the below error.Please find the table format below.I am trying to fetch ( 2 and 3 column)
from temp.txt after index.I am trying to ingnore first two line.
As I mentioned,I got error when I add \n into code
Error : Use of uninitialized value in concatenation (.) or
string at temp.pl line 10, <$fh2> line 300.
#! /usr/bin/perl
use strict;
use warnings;
my #data;
open(my $fh2,'<',"temp.txt") or die "Could not open file";
while(my $line =<$fh2>){
#data =split(/\s+/,$line);
print "$data[2] $data[3]";
print "\n";
}
Table format is:
$DATA1 SOURCE='XtA' VERSION='G-2014.06'
.TITLE '%curve%'
index temp1 temp2 temp3
alter#
1 -1.5750000e+00 -3.2053667e+00 -4.0000000e+01
1.0000000e+00(temp4)
2 -1.5272727e+00 -2.9323414e+00 -4.0000000e+01
1.0000000e+00
3 -1.4795454e+00 -2.6579232e+00 -4.0000000e+01
1.0000000e+00
....................................upto 99
temp1 temp2
<val1> <val2>
After you split do:
if (scalar(#data)==4) {
THEN YOUR PRINT CODE
}
Because you don't know if $data[3] or $data[2] exists.
You need to give us the full error. You should edit the question and then cut and paste the exact error, including the line number. Then, tell us what line in the file that is.
Chances are that in some of the data in that file there are fewer than 4 fields. Therefore, the split returns 3 or fewer scalars into #data. Then, when you try to refer to $data[3], that's an uninitialized value.