Why won't this example from 'Learning Perl 6th Edition' run? - perl

I am stuck on chapter 2 exercise 2 page 42 of Learning Perl 6th Edition.
I copied the code example for the problem from page 296. I am using Perl version 5.10.1 on Ubuntu 11.04. I get errors that I cannot figure out could someone please help? I will list the code and the error message below.
#!/usr/bin/perl -w
$pi = 3.141592654;
print "What is the radius? ";
chomp($radius = <STDIN>);
$circ = 2 * $pi * $radius;
print "The circumference of a circle of radius $radius is $circ.\n";
The error I get is:
./ex2-2: line 3: =: command not found
Warning: unknown mime-type for "What is the radius? " -- using "application/octet-stream"
Error: no such file "What is the radius? "
./ex2-2: line 5: syntax error near unexpected token `$radius'
./ex2-2: line 5: `chomp($radius = <STDIN>);'

You are executing the Perl script using your shell instead of perl. Based on the fact that the line numbers are off by one, I suspect the cause of the problem is a blank line before the shebang (#!) line. #! must be the first two bytes of the file. Delete this blank line.
If that's not the problem, then perhaps you executed your script using
. ex2-2
or
sh ex2-2
when you should have used
perl ex2-2
or
ex2-2 # if "." is in your $PATH
or
./ex2-2
The last two requires that you make the script executable (chmod u+x ex2-2).

It would help if you copied and pasted exactly what you executed. Notice that the line numbers are different in the example below:
$ cat x.pl
#!/usr/bin/perl -w
$pi = 3.141592654;
print "What is the radius? ";
chomp($radius = <STDIN>);
$circ = 2 * $pi * $radius;
print "The circumference of a circle of radius $radius is $circ.\n";
$ sh x.pl
x.pl: line 2: =: command not found
x.pl: line 3: print: command not found
x.pl: line 4: syntax error near unexpected token `$radius'
x.pl: line 4: `chomp($radius = <STDIN>);'
$
This was with Bash 3.x on MacOS X 10.7.1.
Given that output, I can confidently diagnose that your script was run as a shell script and not as a Perl script; bash was used to run it.

Related

Perl command executing good when run on command line but not working in the Perl script

Below is the code I'm trying to execute. I have mentioned the line 266 in the code. I have added that code to remove the blank lines in the log file. I'm not sure whether we can run the perl command inside a Perl script. Is there another way that I can run this so that I can remove the blank lines in the log file?
Below is the error I'm getting while running through the Perl script:
syntax error at ./reportJBossErrors.pl line 266, near "n -e "
Execution of ./reportJBossErrors.pl aborted due to compilation errors.
Here is a portion of the code, showing line 266:
sub main {
readConfiguration($config_file);
$short_hostname = `hostname | cut -f 1 -d.`;
chomp $short_hostname;
getFileandInstance($short_hostname);
$yesterday = getYesterday();
validateEnvironment();
$log_file = getLogFile($FMASK,$yesterday);
perl -i -n -e "print if /\S/" $log_file; # 266 line. This is where I'm getting the compilation error
processFile($log_file);
$html_out = writeEmail();
sendEmail($CONFIG{"FROMADDR"},$CONFIG{"TOADDR"},"Normal",
"JBOSS",$short_hostname,$log_file,$CONFIG{ENVTYPE},$html_out);
}
You can not call the perl command inside a Perl program as if it were a Perl builtin function. You can use system to run an external command:
my $cmd = 'perl -i -n -e "print if /\S/"';
system "$cmd $log_file";
You need to be careful of quoting. Since you have a file name/path in the Perl variable $logfile, which you want to interpolate, that can go inside double quotes. Since you do not want to interpolate \S, that should go in single quotes.
You cannot invoke the perl executable inside a Perl program as if it were a Perl builtin function. Instead, use the list form of system to run an external command. Don't forget to check if the command succeeded:
my #cmd = (perl => '-i', '-n', '-e', 'print if /\S/', $log_file);
system(#cmd) == 0
or die "system #cmd failed: $?";
In general, I would recommend using the full path to perl rather than relying on $PATH.
Also, if you need to keep track of status etc, use Capture::Tiny to get both STDOUT and STDERR of the command you are running so that you can log error information.

Perl line by line tracing activate / deactivate per file

I have lot of Perl code lines to fix bugs, Perl is closed book for me.
I am looking to trace the code flow using following strategy in shebang:
#!/usr/bin/perl -d:Trace
~$ ./trialPerlTracing.pl
>> ./trialPerlTracing.pl:12: print "Statement 1 at line 4\n"; Statement 1 at line 4
>> ./trialPerlTracing.pl:13: print "Statement 2 at line 5\n"; Statement 2 at line 5
>> ./trialPerlTracing.pl:14: print "Call to sub x returns ", &x(), " at line 6.\n";
>> ./trialPerlTracing.pl:20: print "In sub x at line 12.\n"; In sub x at line 12.
>> ./trialPerlTracing.pl:21: return 13; Call to sub x returns 13 at line 6.
>> ./trialPerlTracing.pl:16: exit 0;
This same strategy did NOT work in the actual software I was trying to trace.
I guess somewhere, they have redirected stdout/stderr, how do I redirect to file of my choice selectively?
Since those shebang lines are just comments to Perl, they work only in main Perl programs, not in Perl modules, so you'd have to insert them into the main Perl scripts or run the programs with perl -d:Trace path_to_script.

BEGIN: command not found in Perl

I have a Perl program but when I am trying to run it, it's throwing the error
*****BEGIN: command not found
+ our =/prod/home01/appl/psoft/epmapp/batch/lib
/prod/home01/appl/psoft/epmapp/batch/bin/FileTransfer.pl: line 4: our: command not found
+ push #INC,
/prod/home01/appl/psoft/epmapp/batch/bin/FileTransfer.pl: line 5: push: command not found
/prod/home01/appl/psoft/epmapp/batch/bin/FileTransfer.pl: line 6: syntax error near unexpected token `}'
/prod/home01/appl/psoft/epmapp/batch/bin/FileTransfer.pl: line 6: `}'*****
I think the error is due to the fact that its trying to run the script via the shell instead of via perl. I have checked all the basic things, like there is no extra line before the she bang in my script. But still i am not able to figure out what the exact prob is.
Here is the start of our code:
#!/usr/bin/perl
BEGIN {
our $lib_path="/prod/home01/appl/psoft/epmapp/batch/lib";
push #INC, $lib_path;
}
I am using Red Hat Enterprise Linux Server release 5.9 environment.
Clearly it is interpreted as bash script. If shebang line is exactly as you pasted it, then it must be something wrong with perl binary. Don't you try to run it invalid way e.g. with "bash test.pl", or "source test.pl" from shell? Anyway the code is syntactically correct.
dtpwmbp:~ pwadas$ cat test.pl
#!/usr/bin/perl
BEGIN {
our $lib_path="/prod/home01/appl/psoft/epmapp/batch/lib";
push #INC, $lib_path;
print $lib_path . "\n";
}
dtpwmbp:~ pwadas$ bash test.pl
test.pl: line 3: BEGIN: command not found
test.pl: line 4: our: command not found
test.pl: line 5: push: command not found
test.pl: line 6: syntax error near unexpected token `}'
test.pl: line 6: `}'
dtpwmbp:~ pwadas$ ./test.pl
/prod/home01/appl/psoft/epmapp/batch/lib
dtpwmbp:~ pwadas$
If you can't stop the perl script being executed as a shell script, then adding the magic lines
eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
if 0; # not running under some shell
below the shebang might help.

"Null filename used" error

#!/usr/bin/perl
{
my $file = shift;
print $file;
require $file;
}
run as ./arg /root/perl/arg getting:
Null filename used at /root/perl/arg line 13.
Compilation failed in require at ./arg line 6.
But the file actually exists,why ??
You have to call your program with one command-line argument:
./getting myfilename
Otherwise you're trying to shift into a non-existent variable!
An alternative would be to refer to the argument directly and add a check:
my $num_args = $#ARGV + 1;
if ($num_args != 1)
{
print "Error!";
exit;
}
my $file = $ARGV[0];
Here's a minimal example code to reproduce your error messages. The actual error in not on the -e line, but in nullfn.pm. You're probably trying to use empty string (undef?) in require on line 13 of the included file (/root/perl/arg). The calling file (./arg) is OK.
-bash$ cat nullfn.pm
#!/usr/bin/perl -w
require "";
1;
-bash$ perl -we 'require nullfn;'
Null filename used at nullfn.pm line 3.
Compilation failed in require at -e line 1.
The problem is that you're doing 2 requires. You've assumed that the "Null filename" error is coming from the first one but its actually coming from the second one.
The first require is in the code you posted at line 6. It gets the value that you passed on the command line" "/root/perl/arg". The second require is in "/root/perl/arg" on line 13. This is not getting a value for some reason. When it gets no value it dies with a "Null filename" error. Then execution goes back to the require at line 6 and perl reports that "Compilation failed".
Here is a modified version of your code that explains what's happening as it goes:
$main::runcount++;
{
print "beginning run number $main::runcount\n";
print "\tARGV has ", scalar #ARGV, " arguments\n";
my $file = shift;
print "\tabout to require file `$file`\n";
require $file;
}
1;
And here's the output when I run it with itself as the only argument:
~$ perl arg arg
beginning run number 1
ARGV has 1 arguments
about to require file `arg`
beginning run number 2
ARGV has 0 arguments
about to require file ``
Null filename used at arg line 9.
Compilation failed in require at arg line 9.
From this its clear that the "Null filename" error is generated by the second require.
For fun I ran the script passing it's own name twice:
~$ perl arg arg arg
beginning run number 1
ARGV has 2 arguments
about to require file `arg`
beginning run number 2
ARGV has 1 arguments
about to require file `arg`
Here you can see that the second run of the script is able to get a value from #ARGV. However, since "arg" was already required we don't get a third run.
Another way i found it to work is to give the complete path to the package in the require statement.

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.