"Null filename used" error - perl

#!/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.

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.

Command line arguments in Perl

I am working on an open source project for GSoC and I have this piece of Perl code with me. I need to create another Perl file for a similar task. However, I am having trouble understanding 3 lines of this file. More specifically, I am not able to understand why the files have $ symbol. I assume it is because they are command line arguments. However, I am not sure. I would like to have the meaning of these 3 lines explained to me.
open(NC, "|nc localhost 17001") || die "nc failed: $!\n";
print NC "scm hush\n(observe-text \"$_\")\n";
print "submit-one: $_\n";
$! and $_ are global variables. For more information you can read here
$_ The default input and pattern-searching space
$! If used in a numeric context, yields the current value of the errno variable, identifying the last system call error. If used in a string context, yields the corresponding system error string.
open(NC, "|nc localhost 17001") || die "nc failed: $!\n";
will run the command nc with the parameter and if it fails it will give you the error message.

Tail command used in perl backticks

I'm trying to run a tail command from within a perl script using the usual backticks.
The section in my perl script is as follows:
$nexusTime += nexusUploadTime(`tail $log -n 5`);
So I'm trying to get the last 5 lines of this file but I'm getting the following error when the perl script finishes:
sh: line 1: -n: command not found
Even though when I run the command on the command line it is indeed successful and I can see the 5 lines from that particular.
Not sure what is going on here. Why it works from command line but through perl it won't recognize the -n option.
Anybody have any suggestions?
$log has an extraneous trailing newline, so you are executing
tail file.log
-n 5 # Tries to execute a program named "-n"
Fix:
chomp($log);
Note that you will run into problems if log $log contains shell meta characters (such as spaces). Fix:
use String::ShellQuote qw( shell_quote );
my $tail_cmd = shell_quote('tail', '-n', '5', '--', $log);
$nexusTime += nexusUploadTime(`$tail_cmd`);
ikegami pointed out your error, but I would recommend avoiding external commands whenever possible. They aren't portable and debugging them can be a pain, among other things. You can simulate tail with pure Perl code like this:
use strict;
use warnings;
use File::ReadBackwards;
sub tail {
my ($file, $num_lines) = #_;
my $bw = File::ReadBackwards->new($file) or die "Can't read '$file': $!";
my ($lines, $count);
while (defined(my $line = $bw->readline) && $num_lines > $count++) {
$lines .= $line;
}
$bw->close;
return $lines;
}
print tail('/usr/share/dict/words', 5);
Output
ZZZ
zZt
Zz
ZZ
zyzzyvas
Note that if you pass a file name containing a newline, this will fail with
Can't read 'foo
': No such file or directory at tail.pl line 10.
instead of the more cryptic
sh: line 1: -n: command not found
that you got from running the tail utility in backticks.
The answer to this question is to place the option -n 5 before the target file

Padre giving an error message about a line that doesn't exist

I have this program that runs OK on Windows with "perl program.pl" in the command prompt or "perl program.pl file1.txt file2.txt".
use strict;
use warnings;
print "there were ",scalar(#ARGV), " arguments passed to this program\n";
if ( scalar(#ARGV) > 1 )
{
print "First Argument: $ARGV[0]\n";
print "Second Argument: $ARGV[1]\n";
}
sub process_file($)
{
my $filename = shift;
print "will process file $filename\n";
#open(INPUT_FILE,"<$filename") || die("could not open $filename");
}
Even after exiting and coming back into Padre, I get this dialog box when I go to run it (it will still run OK if I click the Yes button):
Warning X Line 31: Using a | char in open without a | at the beginning
or end is usually a typo. Do you want to continue?
[YES] [NO]
I use or instead of || with open. As an aside, I have always been told that the three argument form of open is what should be used. I do know that Perl doesn't always reference the line that has the problem. Of course I am taking for granted that the commented out line is the line in question and you commented it out as a test?

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.