Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I use the below code to handle upload file through perl cgi. I try fileparse but gives path error. What am I doing wrong here?
exec fail through below error.
fileparse(): need a valid pathname at ./testupload.cgi line 15
Any inputs?
Source:
use strict use CGI;
use CGI::Carp qw ( fatalsToBrowser );
use File::Basename;
$CGI::POST_MAX = 1024 * 5000;
my $safe_filename_characters = "a-zA-Z0-9_.-";
my $upload_dir = "/home/test/Desktop/uploads";
my $query = new CGI;
my $filename = $query->param("textfile");
my ($name, $path, $extension) = fileparse($filename, '\..*');
$filename = $name.$extension; $filename =~ tr/ /_/;
$filename =~ s/[^$safe_filename_characters]//g;
if ($filename =~ /^([$safe_filename_characters]+)$/)
{ $filename = $1; }
else
{ die "Filename contains invalid characters"; }
Looking at the source for File::Basename, it seems that you'll only get that error message if you pass an undefined value as the first argument to fileparse(). So it definitely looks like $filename is undefined. Which means that your line:
my $filename = $query->param("textfile");
Isn't doing what you think it is. Perhaps you're not passing a value for that CGI parameter. Perhaps you've got the name of the form input wrong. But without seeing how you are calling your program, it's impossible to be any more help.
A few more comments on your code:
use strict use CGI; - presumably this gives a syntax error
Please use CGI->new in place of new CGI
The second (and subsequent) arguments to fileparse() should be file extensions. \..* doesn't look much like a file extension to me
You remove all of the unsafe filename characters from $filename and then check to see if there are any unsafe filename characters in $filename. Is that really what you intended?
And then there's the standard advice that learning CGI in 2014 is a lot like learning to use a typewriter. It'll work, of course, but people are going to think you're a bit old-fashioned and strange :-)
Related
I was reading how-do-i-read-in-the-contents-of-a-directory and wanted to find out more about doing it without opening and closing directories as shown in #davidprecious' answer. Tried to read up on DirHandle (hoped for more explanation and example) and several other places simply redirected me to the same perldoc page. Still unsure about where to stipulate the path to read.
Say if I wanted the contents of "E:\parent\sub1\sub2\" and put that into a string variable like $p, where do I mention $p when using Dirhandle?
Would appreciate some guidance. Thanks.
Personally, I'd suggest that's too complicated, and what you probably want is glob:
#!/usr/bin/env perl
use strict;
use warnings;
foreach my $file ( glob "E:\\parent\\sub1\\sub2\\*" ) {
print $file,"\n";
}
Although note - glob gives you the path to the file, not the filename. That's (IMO) generally more useful, because you can just pass the result to open, where if you're doing a readdir you get a file name and need to stick a path on it.
However if you do want to persist with doing it via DirHandle:
#!/usr/bin/env perl
use strict;
use warnings;
use DirHandle;
my $dir_handle = DirHandle -> new ( "C:\\Users\\Rolison\\" );
while ( my $entry = $dir_handle -> read ) {
print $entry,"\n";
}
Don't use $p as a variable name - single character variable names are almost always bad style.
It's probably worth pointing out that Windows is quite happy to use forward slashes (/) as directory separators - which avoids having to have all those ugly double backslashes.
my $dir_handle = DirHandle->new('E:/parent/sub1/sub2/');
while ( my $entry = $dir_handle->read ) {
say $entry;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I want to fill the folder with copies of the same file that would be called differently. I created a filelist.txt to get filenames using Windows cmd and then the following code:
use strict; # safety net
use warnings; # safety net
use File::NCopy qw(copy);
open FILE, 'C:\blabla\filelist.txt';
my #filelist = <FILE>;
my $filelistnumber = #filelist + 1;
my $file = 0;
## my $filename = 'null.txt';
my $filename = $filelist[$file];
while( $file < $filelistnumber ){
copy('base.smp','temp.smp');
rename 'temp.smp', $filename;
$file = $file + 1;
};
If I try renaming it into 'test.smp' or whatever, it works. If I try the code above, I get this:
Use of uninitialized value $filename in print at blablabla/bla/bla.pl line 25, <FILE> line 90.
What am I doing wrong? I feel there's some kind of little mistake, a syntax mistake probably, that keeps evading me.
First, here's some improved code:
use strict;
use warnings;
use File::Copy;
while (<>) {
chomp;
copy('base.smp', $_) or die $!;
}
You'll save it as script.pl and invoke it like this:
$ perl script.pl C:\blabla\filelist.txt
In what ways is this code an improvement?
It uses the core module File::Copy instead of the deprecated File::NCopy.
It uses the null filehandle or "diamond operator" (<>) to implicitly iterate over a file given as a command line parameter, which is simple and elegant.
It handles errors in the event that copy() fails for some reason.
It doesn't use a while loop or a C-style for loop to iterate over an array, which are both prone to off-by-one errors and forgetting to re-assign the iterator, as you've discovered.
It doesn't use the old 2-argument syntax for open(). (Well, not explicitly, but that's kind of beyond the scope of this answer.)
What am I doing wrong? I feel there's some kind of little mistake, a
syntax mistake probably, that keeps evading me.
A syntax error would have resulted in an error message saying that there was a syntax error. But since you asked what you're doing wrong, let's walk through it:
use File::NCopy qw(copy);
This module was last updated in 2007 and is marked as deprecated. Don't use it.
open FILE, 'C:\blabla\filelist.txt';
You should use the three-argument form of open, use a lexical filehandle, and always check the return values of system calls.
my #filelist = <FILE>;
Rarely do you need to slurp an entire file into memory. In this case, you don't.
my $filelistnumber = #filelist + 1;
There's nothing inherently wrong with this line, but there is when you consider how you're using it later on. Remember that arrays are 0-indexed, so you've just set yourself up for an out of bounds array index. But we'll get to that in a second.
my $filename = $filelist[$file];
You would typically want to do this assignment inside your loop, lest you forget to update it after incrementing your counter (which is exactly what happened here).
while( $file < $filelistnumber ){
This is an odd way to iterate over an array in Perl. You could use a typical C-style for loop, but the most Perlish thing to do would be to use a foreach-style loop:
for my $element (#array) {
...
}
Each element of the list is localized to the loop, and you don't have to worry about counters, conditions, or array bounds.
copy('base.smp','temp.smp');
Again, always check the return values of system calls.
rename 'temp.smp', $filename;
No need to do a copy and a rename. You can copy to your final destination filename the first time. But if you are going to rename, always check the return values of system calls.
};
Blocks don't need to be terminated with a semicolon like simple statements do.
You should avoid using bareword file handles. When opening you should open using a file reference like and make sure you catch it if it fails:
open(my $fh, '<', 'C:\blabla\filelist.txt') or die "Cannot open filelist.txt: $!";
The $fh variable will contain your file reference.
For your problem it looks as though your filelist.txt must be empty. Try using Data::Dumper to print out your #filelist to determine it's contents.
use Data::Dumper;
EDIT:
Looks like you are also wanting to be setting the $filename variable to the next one in the list for each iteration, so put $filename = $filelist[$file]; at the beginning of your loop.
Your problem could be that you are looping too far? Try getting rid of the + 1 in my $filelistnumber = #filelist + 1;
I'm new to using modules in Perl. My head is exploding right now and i would like to know what is wrong in here:
#!/usr/bin/perl
use strict;
use Mail::Mailer;
my $from_adress = "email\#xxxxx.com";
my $to_adress = "email\#hxxxx.com";
my $subject = "There goes bananas\n";
my $body = "Here is the bananas";
my $server = "smtp.gmail.com";
my $mailer = Mail::Mailer->new("smtp", Server => $server);
$mailer->open({
From => $from_adress,
To => $to_adress,
Subject => $subject,
});
print $mailer $body;
$mailer->close();
open(F, '>>', $Mail::Mailer::testfile::config{outfile});
print F #_;
print #_;
close (F);
Sorry to post the whole script but i'm not sure where it went wrong. I don't get any print from #_ variable. I would love to receive advises on how to improve in using modules in Perl and how i can get better at it.
Thanks in advance.
Well done for using strict in your code. For extra credit, add a use warnings line too.
I can't see any obvious problems with the way you're using the module. Do you think there's something wrong? Is the email not being sent?
If you're not getting the email, then I'd suggest that your first step should be to follow the example in the documentation and change the close line to:
$mailer->close
or die "couldn't send whole message: $!\n";
I wonder if the problem (if there is one) is that you're using Google's SMTP server and you don't have permission to do that. Perhaps you need to authenticate first.
A few other points about your code.
There is no need for all of your set-up variables to be initialised with double-quoted strings. And if you switch to single-quoted strings then you no longer need to escape the #s in the data. You would need double quotes to put the newline in $subject, but I've removed that as email subject lines rarely contain newlines.
my $from_adress = 'email#xxxxx.com';
my $to_adress = 'email#hxxxx.com';
my $subject = 'There goes bananas';
my $body = 'Here is the bananas';
my $server = 'smtp.gmail.com';
The last four lines of your code are confusing in many ways. I'm not really sure what you're trying to achieve there. I'll point out two things though. Firstly, we generally use lexical filehandles these days. If you're learning from a source that uses bareword filehandles, then I'd worry slightly about its age. So the file opening line should look like this:
# $f is, of course, a terrible name for a variable
open(my $f, '>>', $Mail::Mailer::testfile::config{outfile});
You are then printing the value of #_. In Perl, #_ contains the arguments to a subroutine. And this code isn't inside a subroutine, so #_ will be empty. So I'm not surprised that you're not getting any output.
Lastly, I'll point out that I find that I enjoy working with email in Perl a lot more when I'm using tools from the Email::* namespace. In particular, I'd use Email::Sender for sending email.
Update: Ok, I've had a closer look at the Mail::Mailer documentation and I think I understand what you're trying to do in the last four lines. I think you're trying to write the mail message data to the file. Is that right?
If it is, then you're misunderstanding the documentation. The way to do that is to change the type that you pass to new(). It needs to be testfile rather than smtp. So change
my $mailer = Mail::Mailer->new("smtp", Server => $server);
to
my $mailer = Mail::Mailer->new("testfile",);
That will write the mail to a file called mailer.testfile and no mail will be sent.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 years ago.
Improve this question
Can anyone tell me why I'm getting this:
usage: gen-non-random.pl <count> <outputfile>
From the code below:
#!/usr/bin/perl -w
#
# Script to generate non random values, to demonstrate a bad randomness graph
# for my "Howto Analyse SessionIDs".
#
# written by:
$version = "0.0.4";
$filename = "gen-non-random.pl";
$usage = "usage: $filename <count> <outputfile>\n";
$count = $ARGV[0] or die ("$usage\n");
$output = $ARGV[1] or die ("$usage\n");
print ("-- $filename Version: $version\n");
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
use Math::Random;
use Digest::MD5 qw(md5_hex);
open (OUT, ">$output") or die ("Can't open $output\n");
for ($i=0; $i<$count;$i++)
{
# generate a random number
$random = random_uniform();
# cut out char 3-9 of $random and put it in $randsub
$randsub = substr($random, 2, 6);
# get seconds and microseconds since epoch
($seconds, $microseconds) = gettimeofday;
# get the last two chars of the seconds and put them into $s
$s = substr($seconds, 8, 2);
# sleep for a while
usleep $randsub;
# put together the last two digits of seconds and the microseconds
$time = $s . $microseconds;
$md5_time=md5_hex($time);
# print out the stuff we put together above
print OUT ("$md5_time\n");
}
close (OUT) or die ("Can't close $output\n");
print ("$count values written to $output\n");
exit;
I am new to programming so i need really simple answer please! I do not own this code I am using for my research paper at University. Also, could someone please explain to me what Usage actually is i can't seem to find a good explanation for it?
Thanks.
You're getting that error because you're not using the program correctly:
usage: gen-non-random.pl <count> <outputfile>
This basically means you have to provide a count and output file as arguments, such as:
perl gen-non-random.pl 42 outfile.txt
This will generate forty-two numbers and output them to the outfile.txt file.
It's the two lines near the start, checking ARGV[0/1] and die-ing if you don't provide them, that are outputting this message and exiting the program.
Hmmm. I can't run the above code because Time::HiRes::ualarm() is not implemented on Windows. That said, it appears to be generating a MD5 has string of the current time (in integer form) after sleeping for a random number of seconds, then dumping the result into a text file. You are getting the usage message mentioned above because the program expects input. Try running it from the command line like so:
perl gen-non-random.pl 10 MyResults.txt
I suspect that will dump 10 HD5 hash results into a file called "MyResults.txt".
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 9 years ago.
Improve this question
I am working on a program that takes information from a CSV file as a source to search with through a text file that has "customer packages". I am getting odd counts on only some of the entries, and I can't seem to figure out what is causing the duplicate counts. Can anyone look through my code and tell me if my logic/syntax is off? (probably is). All i am trying to accomplish is to count the total occurances in the text file of an entry in the csv file (packageid,package_description)
Thanks for the help! im going nuts over here.
#!/usr/bin/perl
use strict;
use Text::CSV;
# Variables already declared in the other PL file ** Remove if consolidating **
my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new(); # Create a Text::CSV object
open (CSV2, "<", $file2) or die $!; #open CSV file for parsing
while (<CSV2>) {
if ($csv2->parse($_)) {
my #columns2 = $csv2->fields(); # Parse CSV and load into an array for each row.
my $packID = $columns2[0];
my $packDESC = $columns2[1];
my $val = 'customer_packages_report.txt';
chomp ($val);
my $cnt=0;
open (HNDL, "$val") || die "wrong filename";
while ($val = <HNDL>)
{
while ($val =~ /$packID - $packDESC/ig)
{
$cnt++;
}
}
#if ($packDESC =~ /\(/g) {
# $packDESC =~ s/\(/\(/g;
#}
print "Total iterations of $packDESC: $cnt\n";
close (HNDL);
# End original code
} # Close IF
} # Close WHILE
close CSV;
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV;
# Variables already declared in the other PL file ** Remove if consolidating **
my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new(); # Create a Text::CSV object
open (CSV2, "<", $file2) or die "I die while opening $file2! $!"; #open CSV file for parsing
while ($each_csv2_line=<CSV2>) {
if ($csv2->parse($each_csv2_line)) {
my #columns2 = $csv2->fields(); # Parse CSV and load into an array for each row.
my $packID = $columns2[0];
my $packDESC = $columns2[1];
my $val = 'customer_packages_report.txt';
chomp ($val);
my $cnt=0;
open (HNDL,"<","$val") or die "wrong filename: $val! $!";
while (<HNDL>){
$cnt++ while (/$packID - $packDESC/ig);
}
#if ($packDESC =~ /\(/g) {
# $packDESC =~ s/\(/\(/g;
#}
print "Total iterations of $packDESC: $cnt\n";
close (HNDL);
# End original code
} # Close IF
} # Close WHILE
# end of script
close CSV;
My recommendations:
Use $HNDL instead of HNDL <- lexical variables for filehandles more better.
Try to catch all mistakes (by defined and ==0 and eq "")
I try to format your code and add some features that i sometimes use. Be better than me and read first Style Coding for Little Perl Monk. And you can be more impressive with this language and write not only writeonly code.
Example (and also a quote):
"The situation is exactly the same for the line-input operator, <>, although Perl does this for you automatically.
It looks like you’re testing the line from STDIN in this while:
while (<STDIN>) {
do_something($_);
}
However, this is a special case in which Perl automatically converts to check $_ for definedness:
while ( defined( $_ = <STDIN> ) ) { # implicitly done
do_something($_);
}
"
Effective Perl Programming, page 24.
You could do a number of things to improve your code:
use warnings;.
Use proper indentation.
Use descriptive variable names. Instead of $file2 (has no meaning, and why is there no file 1?), use $package_file or whatever makes sense.
if you are already using Text::CSV, you can use $csv->getline() to go through the file line by line. This will simplify your code. See the documentation for an example.
chomp($val) removes a newline from the end of a string. You are using it on a string literal you just declared, which has no newline. That doesn't make sense.
Never use the same variable ($val) to do two completely different things. This is extremely confusing.
Might the variables that you are interpolating in the regex contain special characters? If so, you need to escape them. For example, if $packDESC contained a period, it would match any character in the regex. To treat the contents of the variable literally, use \Q..\E, as in this example: /\Q$packID - $packDESC\E/ig.
You are opening customer_packages_report.txt and going through it line-by-line on every line of the csv file. You could simplify this by reading it in once and storing the results in an array.
You don't need a while loop to count matches: $cnt = () = /$packID - $packDESC/ig;. This puts the match in array context, returning an array of matches, then puts it back in scalar context to count the matches. A little bit tricky, but simpler.
It's hard to say exactly what is causing your problem without seeing the data. Might you have some unnecessary repetition that stems from your nested looping over both files? I would start by rewriting to improve your code, then see if the problem still exists.
Your code seems to compile with perl -c without errors, so that's good. If I were to guess, I would assume your problem lies in having meta characters in some of your fields. The regex /$packID - $packDESC/ is vulnerable to meta characters. For example
my $str = "foo? bar";
$str =~ /$str/; # returns false, because ? is a meta character
In the above example, the question mark ? is a quantifier which affects whatever comes before it, so that o? means "0 or 1 o". To solve the meta character problem, use the \Q ... \E escape:
$str =~ /\Q$str/; # will now match
Terminating the escape sequence with \E is optional.
Some other things to note:
It is very good that you use use strict. You should also always use warnings. Not doing so is not removing the issues with your code, only hiding them.
You create a Text::CSV object with default settings. Depending on your input, that may or may not be appropriate. Setting binary => 1 is recommended in the documentation.
Using the parse() function may not be the best option, the documentation has good things to say about getline.
As loldop points out in the comments, you are reusing $val to read from your file. While technically that should work, it is asking for trouble.
Style and practice notes and practical tips:
Using three-argument open and lexical file handles is a good thing to do. Three-argument in essence means to use an explicit open mode, which makes your script safer to use. Using lexical file handles means that you will not have global scope on your file handle, which is a good thing.
This code
my #columns2 = $csv2->fields();
my $packID = $columns2[0];
my $packDESC = $columns2[1];
Can be written like this
my ($packID, $packDESC) = $csv2->fields();
You are chomping $val right after you assign it. That is redundant, because chomp by default only removes newlines from the end of your strings, and you did not add any such. It doesn't change anything, but not required here. If you read something from stdin or a file, you would probably want to use chomp, though.
Using die without referring to the error $! is a sure way to make yourself annoyed.
Do not underestimate how much easier it becomes to write code when you use proper indentation. Use a text editor with automatic indentation and colouring. I can warmly recommend vim (gvim if you are using windows). Though it has a learning curve, is is a powerful editor that also often comes already installed on many systems.
Since so many people have already commented on your program itself, I'm going to talk about how you can become a better Perl programmer, and help write in such a way that will help eliminate many of your issues.
Take a look at Perl::Tidy and run your program thorough that. That will help improve your syntax and Perl and will help you catch a lot of the various issues you're having.
Also, you should get a copy of Perl Best Practices which is where most of Perl Tidy is taken from. And, as someone already referenced Effective Perl Programming is another excellent book.
The big issue with Perl is that few people learn it. Most are tossed into a situation where we had to pick it up ourselves. Plus, Perl is a fairly old and rather crufty language. Most Perl books still lean heavily on Perl 3.x ways of programming and fail to mention such basics as using use strict; and use warnings;.
You combine old programming practices, with most people learning Perl by hacking their way through old programs with old syntax (and probably written by people who learned Perl by hacking their way through even older programs), and you can see why Perl has a reputation of being a write-only language.
You may want to use the getline method from Text::CSV, which saves a few lines of code.
The problem is likely to be because you have regex metacharacters in the strings you are searching for. Escape them with \Q...\E in the regex so that they are taken literally. In the rewrite below I have also added \s* instead of a literal space, just in case there isn't exactly one space on either side of the hyphen.
I have also changed the filehandles to lexical ones, which have the advantage that they will be closed automatically when the handle goes out of scope.
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV;
my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new();
open(my $csv_fh, '<', $file2) or die $!;
while (my $row = $csv2->getline($csv_fh)) {
my ($packID, $packDESC) = #$row;
my $val = 'customer_packages_report.txt';
chomp($val);
open(my $fh, '<', $val) or die "wrong filename";
my $cnt = 0;
while ($val = <$fh>) {
while ($val =~ /\Q$packID\E\s*-\s*\Q$packDESC\E/ig) {
$cnt++;
}
}
print "Total iterations of $packDESC: $cnt\n";
}