Output correct line number for Perl script wrapped in batch on Windows? - perl

Is there any specifier to tell Perl interpreter that the real Perl scripts starts at some line, then the output will have correct line number from interpreter, like interpret error message? For example, the line number information in below script is incorrect.
#perl -x "%~f0" %*
#exit /b %errorlevel%
#!perl
use strict;
printxxx "Perl Script\n"; # interpreter will output error for this line with **incorrect line number**

You can use the line directive, which is a special form of comment that looks like # line 99 and dictates the line number that perl will assign to the following line.
For instance
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
STDOUT->autoflush;
# line 100
say "line ". __LINE__;
die;
output
line 100
Died at E:\Perl\source\line.pl line 101.
It is also possible to add a filename after the line number, which similarly dictates the source file name that perl will report.

Related

Why is the STDOUT line printed after the STDERR line?

I completly don't understand this behaviour.
I have a very simple Perl script:
#!/usr/bin/perl
use strict;
use warnings;
print "line 1\n";
print STDERR "line 2\n";
If I run it from console I will get what I expect:
$ perl a.pl
line 1
line 2
But if I redirect it to file, I will get the lines in the reverse order:
$ perl a.pl &> a
$ cat a
line 2
line 1
How can I capture all the outputs STDOUT+STDERR to the file with the same
order I get in the console?
This is an effect of buffering. When outputting to a terminal, Perl (just as stdio) uses line based buffering, which will give the expected effect. When outputting to a file, it uses block buffering (typically 4k or 8k blocks), hence it will only flush on program end.
The difference you're observing is because STDERR is unbuffered, unlike most other handles.
The easiest way to circumvent this issue is to enable autoflushing on STDOUT, using the $| special variable. See perlvar for more details.
Don't use $|, use autoflush instead.
STDOUT->autoflush(1);
STDERR->autoflush(1);
See http://perldoc.perl.org/IO/Handle.html

Change the position of program lead to syntax error

I have a program called shuffle.pl . When I use perl shuffle.pl Input Shuffled to execute , it success work and show no error .
I create a directory called ./tools under my home directory , and I set this path to .cshrc . So I can execute the program without typing perl to execute . ( This is my first time to do this , maybe some wrong in here)
But when I move the shuffle.pl to ~/.tools and execute . it show I have error in line 5 . But if I use perl ~/.tools/shuffle.plit can work . So it means it should have no syntax error in my program ,But why it can't work after I put my program to ~/.tools
error message
.tools/shuffle.pl: 5: Syntax error: "(" unexpected
.cshrc
set path = (. ~ ~/.tools /sbin /bin /usr/sbin /usr/bin /usr/games /usr/local/sbin /usr/local/bin )
thanks
here is my program
#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw(first max maxstr min minstr reduce shuffle sum);
open(my $fh,"<","$ARGV[0]");
my #Lines = readline($fh);
my #Shuffled = shuffle(#Lines);
close $fh;
open(my $shuf,">","$ARGV[1]");
print $shuf #Shuffled;
close $shuf;
The shebang is used to tell which interpreter should be used for this script. For this to work, the magic number #! has to appear at the immediate beginning of the file. Otherwise, the default interpreter is used.
In this case, the shebang was preceded by a few empty lines. They have to be removed.
The shebang is not parsed when an explicit interpreter is used to execute the file, E.g. in $ perl script.pl.
It is only important when launched as executable: ./script.pl. In that case, the kernel is left to figure out what to do with it: Load into memory as compiled program? Launch an interpreter? Which one? Magic numbers like #! resolve this.
In general, if the shebang doesn't work, the following possible errors can be checked:
An UTF byte order mark precedes the #!.
Diagnosis: A hexdump shows FE FF at the beginning.
Solution: configure your editor to store files without a BOM
The script is encoded in such a way that the beginning does not decode to #! as ASCII.
Diagnosis: The file does not begin with #! when opened as ASCII or does not begin with 23 21 in a hexdump. Or your editor shows UTF-16 or UTF-32 as the encoding.
Solution: Store the script in ASCII-compatible encoding. UTF-8 is an especially good choice.
Non-native line endings can be confused to be part of the executable name. E.g. with windows line endings, the shebang in
#!/usr/bin/perl
print 1;
could be taken as the interpreter name "/usr/bin/perl\r". Many filesystems allow line endings inside filenames.
Diagnosis: A hexdump shows something other than a space (20) or newline (0A) after the executable name.
Solution: Convert line endings to Unix.
A few general tips:
You should have -w on the shebang line to catch warnings.
You should probably use strict; also.
Don't put double quotes around "$ARGV[0]" and "$ARGV[1]" because they serve no purpose.
Use "do or die" syntax on the file opens, e.g.:
open (File, "<", $ARGV[0]) || die "File open error: $!";
Do those things and I am pretty sure the solution will appear rapidly.

Why is "use" not allowed, as in "use strict;" in Perl 5.14?

I am trying to use the following conventions I have been instructed to use for good/proper/safe Perl code for my "Hello, World!" Program:
use strict;
use warnings;
I have created and successfully run the following "Hello World" program using (Strawberry) Perl 5.12 on my main Windows 7 OS:
!#/usr/bin/perl
use strict;
use warnings;
print "Hello, World!\n";
What I got back, as expected, was "Hello, World!".
What struck me as very odd was that the same program run in terminal on my virtualized Linux Mint 14 OS, using Perl 5.14, produced the following error:
"use" not allowed in expression at /PATH/hello_world.pl line 2, at end of line
syntax error at /PATH/hello_world.pl line 2, near "use strict"
BEGIN not safe after errors--compilation aborted at /PATH/hello_world.pl line 3.
I created other "Hello World" programs subsequently without either the use strict; or use warnings; lines, and also one with the -w, which I had seen in some tutorials, indicating, if I am not mistaken, that warnings would be turned on.
Both of my alternate versions worked properly in that they produced my expected result:
Hello, World!
What I cannot be sure of is if I need the use statements in Perl programs from version 5.14 and up or if it is just fine to write the -w at the end of my first line.
I would like to think that I could use a consistent header, so to speak, in all of my Perl programs, whether they are Windows or Linux, Perl 5.12 or 5.14 or otherwise.
Your image shows that all of your scripts start with !#/usr/bin/perl. This is wrong. It is not a valid she-bang line, it is read as negation ! followed by a comment #. The parsing will continue and with script1.pl perl will execute ! print "Hello world.\n";. This will print Hello world and negate the result of print ... not really useful, but valid perl.
In script2.pl perl sees ! use strict; and this is a compile time error and therefor perl fails and reports the error for the line use strict;.
So if you use correct she-bang lines, all three scripts will work as designed.
Edit (test scripts added):
script1.pl
!#/usr/bin/perl
print "Hello world.\n" ;
Calling perl script1.pl gives
Hello world.
script2.pl
!#/usr/bin/perl
use strict;
use warnings ;
print "Hello world.\n" ;
Calling perl script2.pl gives
"use" not allowed in expression at script2.pl line 3, at end of line
syntax error at script2.pl line 3, near "use strict "
BEGIN not safe after errors--compilation aborted at script2.pl line 4.
Using the correct syntax script3.pl
#!/usr/bin/perl
use strict ;
use warnings ;
print "Hello world.\n" ;
Calling perl script3.pl gives
Hello world.
You did something like
use warnings
use strict;
instead of
use warnings;
use strict;
Actually, I think it might be a line ending issue. You have LF where you should have CR LF or vice-versa. I've seen this cause Perl to think the code starts halfway through the shebang line (e.g. perl use strict;)
As mentioned elsewhere, the code you posted and the code you used is different. You actually used
!use strict;
due to a bad shebang line.
!#/u... # Negation followed by a comment
should be
#!/u... # Shebang

Perl 'system' failure messages

Say I have this perl "program" called simple.pl:
#!/usr/bin/perl
use xyz; # xyz is bogus and doesn't exist
And I also have this "program", called simple2.pl:
#!/usr/bin/perl
system("simple.pl");
my $abc = `simple.pl`;
printf("abc %s\n", $abc);
for both system and backtick, I get this message:
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 7.
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 9.
Not very useful for the user calling simple2.pl. Is there a way to get a more useful message?
Note. simple.pl does exist in the current directory. The real problem is that simple.pl doesn't compile. simple2 responds by saying simple doesn't exist. it's a misleading message.
If I had a way to even capture the compile message that would be a start.
This means system couldn't find an executable named "simple.pl" on your PATH. If your simple.pl is in the current directory, you could try to change "simple.pl" to "./simple.pl".
Actually, I don't see how to make this message more descriptive. If you were perl, how would you report this error?
BTW, I wouldn't try to run "simple2.pl" from inside of simple2.pl :)
Yes, check to see if the file exists and is executable, and if it isn't, print a more descriptive message.
unless (-ex $filename) {
print "I am unable to execute file $filename.";
}
If perl say it can't find the file, then it can't find the file. And the problem is more your code. Look at this example.
sidburn#sid:~/perl$ cat test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use xyz;
sidburn#sid:~/perl$ cat test2.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('test.pl');
sidburn#sid:~/perl$ cat test3.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('./test.pl');
If you execute test2.pl you get:
sidburn#sid:~/perl$ ./test2.pl
Can't exec "test.pl": No such file or directory at ./test2.pl line 4.
If you execute test3.pl you get:
sidburn#sid:~/perl$ ./test3.pl
Can't locate xyz.pm in #INC (#INC contains: /home/sidburn/perl510/lib/5.10.1/i686-linux /home/sidburn/perl510/lib/5.10.1 /home/sidburn/perl510/lib/site_perl/5.10.1/i686-linux /home/sidburn/perl510/lib/site_perl/5.10.1 .) at ./test.pl line 4.
BEGIN failed--compilation aborted at ./test.pl line 4.
If you don't provide a relative or absolute path then perl lookup the command in your $PATH environment variable. If it is not there it can't find the file.
You need to provide "./" if it is in the current directory. But note "current directory" doesn't mean the directory where your script relies.
If you want the later then you probably want to do a
use FindBin;
with this you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Spec::Functions;
my $exe = catfile($FindBin::RealBin, 'test.pl');
print $exe, "\n";
system($exe);
if you want to check if system returns correctly, you need to check the return value from the system() command or $? later that holds the value.
if ( $? != 0 ) {
die "Cannot execute $exe.\n";
}
if you want to suppress messages from your program you need to redirect STDOUT, STDERR before starting your program with system().
Or use something like IPC::System::Simple
Or IPC::Open3 (in the core).
Bonus points for enabling the warnings pragma! Have an upvote!
You want to use backticks or qx// to capture the output of an external program, not system. To substitute your own error message that will make sense to your users (more points for you!), then you might do something as in
#! /usr/bin/perl
use strict;
use warnings;
no warnings 'exec';
chomp(my $abc = `simple2.pl`);
if ($? == 0) {
printf("abc %s\n", $abc);
}
else {
die "$0: unable to calculate abc\n";
}
In case you're unfamiliar, $? is
$CHILD_ERROR
$?
The status returned by the last pipe close, backtick command, successful call to wait or waitpid, or from the system operator.
When $? is zero, it indicates success.
Remember that the warnings pragma is lexical, so rather than disabling the warning for the whole program, you might do it for just one sub:
sub calculate_abc {
no warnings 'exec';
# ...
}
If you are trying to execute something you know is a Perl script, why not invoke the interpreter directly rather than dealing with the system knowing how to execute the file?
my $file = 'simple.pl';
-e $file or die "file '$file' not found";
system "perl $file";
# or
print `perl $file`;
to run with the same installation of perl that is running your current script:
system "$^X $file"; # or `$^X $file`
$^X is a special Perl variable that contains the file name of the running interpreter.
I had the exact same issue and figured out that perl wasn't installed. So the bash script was trying to execute the perl without an interpreter.
ls /usr/bin/perl
Try specifying the full path to the "simple.pl" file.

What's the purpose of perl's #line directives?

Line directives (#line) are used to reconfigure perl's idea of the current filename and line number. When is this required to get right filename and line number (in an error message)?
Usually such markers are put into code that has been pre-processed or mechanically generated in order to refer back to the human-generated source.
For example, if there was a program that converted Python to Perl, it might insert a
# line 812 "foo.py"
so that error messages would refer to the original Python code which would make more sense to the programmer.
They're useful when wrapping a Perl script in another file, like pl2bat does. Perl doesn't see the batch commands at the beginning of the file which throws off its idea of line numbers. A #line directive at the beginning of the Perl source compensates for this.
I've seen several times that people incorrectly write the current line's number into the #line directive. It should contain the next line's number. Example code of linetest.pl (using a ksh wrapper to set an environment variable for the perl script):
1 #!/usr/bin/ksh
2 MY_ENV_VAR='something'
3 export MY_ENV_VAR
4 /usr/bin/perl -x $0 $# 2>&1
5 exit $?
6
7 #!/usr/bin/perl
8 #line 9
9 print "MY_ENV_VAR is $ENV{MY_ENV_VAR}\n";
10 die "This is line 10.";
Run the script and check the result:
$ ./linetest.pl
MY_ENV_VAR is something
This is line 10. at ./linetest.pl line 10.
You can see that line numbers are matching after writing #line 9 on line 8.
In addition to the already mentioned reasons perl has a (strongly discouraged) -P option that runs the Perl file through a C preprocessor before it is executed. Since most C preprocessor's will use line directives when they include or remove part of a file so any errors will be reported from where they were located in the original source instead of the processed source.
Line directives can also be very useful if you are generating code in strings that is then passed to eval. Normally if there is a warning or error in such code you get an error reported like "died at (eval 1) line 1." Using line directives you can supply a useful file name and line number.
The #line directive is also very helpful when doing perl -e in a shell script. I write
perl -e '#line X
more perl code here...
'
where X is the current shell script line +1 so that any Perl errors tell me the shell line where the failed Perl statement is.