Perl 'system' failure messages - perl

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.

Related

Unable to execute perl script in cmd without invoking perl.exe

I am trying to execute a perl script to delete file1.txt in a directory.
When I execute the perl script using command prompt, I faced an error : Use of uninitialized value in chdir at C:/Debug/test.pl line 7.
Example:
C:\Debug>test.pl C:\Debug
However if I invoke perl in front of test.pl, the perl script is executed successfully and file1.txt was deleted.
Example:
C:\Debug>perl test.pl C:\Debug
Please find test.pl code as follow:
use strict;
use warnings;
use File::Copy;
my ($working_dir) = #ARGV;
chdir $working_dir or die "Can't change directory$!";
unlink "file1.txt";
I have mks_toolkit v8.7.5 with perl.exe installed. The .pl extension is associated with "C:\Program Files (x86)\MKS Toolkit\mksnt\perl.exe" "%1" %*.
I have no problem execute hello.pl (Hello World) without invoking perl in front of it.
Example:
C:\Debug>hello.pl
Anyway, users who face this problem can try to check the properties of your file, make sure the file is not read only and has full admin access.

How to pass command line arguments along with perl modules using perl?

I treid one of my previous example to pass input and output files via command line arguments using perl?
Previous example:
[Is it possible to add the file out in the File::Find::Rule using perl?
code which i tried to add input and output file as command line arguments:(generate.pl).I got struck how to include command line arguments to read and copy the files along with perl modules.
use strict;
use warnings 'all';
use CGI qw(:all);
#generate the user file arguments
use Getopt::Long 'GetOptions';
use File::Path qw( make_path );
use File::Copy qw( copy move);
use File::Find::Rule qw( );
GetOptions(
'prjroot=s' => \my $input_dir,
'outdir=s' => \my $output_dir,
);
my %created;
for my $in (
File::Find::Rule
->file()
->name(qr/^[^.].*\.yfg$/)
->in($input_dir)
) {
my $match_file = substr($in, length($input_dir) + 1);
my ($match_dir) = $match_file =~ m{^(.*)/} ? $1 : '.';
my $out_dir = $output_dir . '/' . $match_dir;
my $out = $output_dir . '/' . $match_file;
make_path($out_dir) if !$created{$out_dir}++;
copy($in, $out);
}
Error occured:
./generate.pl: line 1: use: command not found
./generate.pl: line 2: use: command not found
./generate.pl: line 3: syntax error near unexpected token `('
./generate.pl: line 3: `use CGI qw(:all);'
perl execution should be as follows:
I should copy the contents from one directory to another directory along with perl modulES(I.E File::Find::Rule)
./generate.pl -prjroot "/home/bharat/DATA" -outdir "/home/bharat/DATA1"
Help me fix my issues .
You miss the perl comment in the first line:
#!<path_to_perl>/perl
use strict;
use warnings 'all';
use CGI qw(:all);
#generate the user file arguments
.....
Your program will also work if you call it with the perl Interpreter in front of your command:
perl ./generate.pl -prjroot "/home/bharat/DATA" -outdir "/home/bharat/DATA1"
You've already seen what the problem is. But I'll just add that perldoc perldiag gives pretty good explanations for any error message you get from Perl. In this case, searching for "command not found" would give you this:
%s: Command not found
(A) You've accidentally run your script through csh or another shell instead of Perl. Check the #! line, or manually feed your script into Perl yourself. The #! line at the top of your file could look like
#!/usr/bin/perl -w
And that's particularly impressive, given that this error isn't actually generated by Perl - but by your shell.

Calling script with the same #INC as the parent script

I want to call an external script with system ($script) or do $script. In my #INC i have some specific modules which I import. How can I call the $script and transfer it the same #INC?
Script1.pm
#importing some libs
#code
$script = "path_to_script";
system ($script);
Script2.pm
use LibFromScript1#INC;
And I get the Error :
Can't locate LibFromScript1 in #INC...
The easiest way is probably to set the PERL5LIB environment variable. That will add a list of directories to the child process's #INC array
Your code would look something like
$ENV{PERL5LIB} = join ':', #INC;
system $script;
This has the disadvantage that the standard directories will also be added to #INC. It shouldn't cause any problems, but it would be best to set PERL5LIB to just the custom directories if you know them at that point.
Note also that perl will ignore PERL5LIB if you are running under the taint flag.
Strictly answering your question you could do $script; although recommended way would be to separate your common program logic into module, and use/require it.
You could use Storable to save #INC in a file and then pick it in other script.
For example you could do something like below.
test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Storable;
store (\#INC, "test2.dump") or die "could not store";
system("perl", "test2.pl", $$) == 0 or die "error";
test2.pl
#!/usr/bin/perl
use strict;
use warnings;
use Storable;
use Data::Dumper;
my $parentpid = shift;
my $ref = retrieve("test2.dump") or die "couldn't retrieve";
print Dumper $ref;
Once you get the #INC in test2.pl as $ref, you can modify #INC in test2.pl to take contents from $ref.

Using commands inside scripts for Perl?

I trying to run a perl command inside a script and here is what I got:
filenumber1.txt:
94088076164765675
The command window looks okay:
C:\Users\Guest\Documents\Prime Numbers>perl -Mntheory=:all -nE "chomp; say next_prime($_);" filenumber1.txt
94088076164765687
I am trying to do this in a script as well called primenumbers.pl and generate another file called log.txt as the output file and filenumber1.txt as the input file:
#!/usr/bin/env perl
use warnings;
use strict;
use feature 'say';
use ntheory ":all";
# open filehandle log.txt
open (my $LOG, '>>', 'log.txt');
# select new filehandle
select $LOG;
perl -Mntheory=:all -nE "chomp; say next_prime($_);" filenumber1.txt
primenumbers.pl is in the same directory as filenumbers.txt and something is wrong when I run the script and I do not know what:
C:\Users\Guest\Documents\Prime Numbers>perl primenumbers.pl
Bareword found where operator expected at primenumbers.pl line 7, near ""say next_prime($_);" filenumber1.txt"
(Missing operator before filenumber1.txt?)
syntax error at primenumbers.pl line 7, near ""say next_prime($_);" filenumber1.txt"
Execution of primenumbers.pl aborted due to compilation errors.
I tried fixing it myself, but I only made matters worse, and there were more problems to fix. Does anyone know how to help me from here please on generating the output file "log.txt" without viewing the input file "filenumber1.txt"? Thanks in advance.
The -n switch to perl just wraps the code in while (<>) { ... }, so, to get a similar effect in your own program, just add the loop yourself (untested code; may contain errors):
#!/usr/bin/env perl
use warnings;
use strict;
use feature 'say';
use ntheory ":all";
# open filehandle log.txt
open (my $LOG, '>>', 'log.txt');
# select new filehandle
select $LOG;
while (<>) {
chomp; say next_prime($_);
}
Run this with
primenumbers.pl filenumber1.txt
and you should be set.

How can I append the file foo2.txt to foo1.txt?

Is there any method to execute foo2.pl from foo1.pl in Perl, and append the foo2.txt to the foo1.txt then create foo3.txt? thanks.
i.e.
foo1.pl
print "Hello"; # output to foo1.txt;
foo2.pl
print "World"; # output to foo2.txt;
How to create foo3.txt file based on foo1.pl.
foo3.txt
Hello
World
Something like append foo2.txt to foo1.txt.
As i know, I can open foo1.txt and foo2.txt, then include the lines in foo3.pl.
print FOO3_TXT (<FOO1_TXT>);
print FOO3_TXT (<FOO2_TXT>);
Is there any good method?
Update my test (ActivePerl 5.10.1)
My foo.pl
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
print "world\n";
my hw.pl (foo.pl and hw.pl at the same directory)
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
print 'hello ';
print `./foo.pl`;
Output
**D:\learning\perl>hw.pl
hello '.' is not recognized as an internal or external command,
operable program or batch file.**
If hw.pl updated {}:
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
print q{hello }, qx{./foo.pl};
Now Output. (a little different for the loacation of hello)
D:\learning\perl>hw.pl
'.' is not recognized as an internal or external command,
operable program or batch file.
hello
[Update].
Fixed. see answer,
run this as a script
perl foo1.pl > foo3.txt;
perl foo2.pl >> foo3.txt;
contents of foo1.pl
!#/bin/perl
print "Hello";
contents of foo2.pl
!#/bin/perl
print "World";
or
simply use the cat command if you are running linux to append foo2.txt to foo1.txt.
Just in case you are being literal about execute foo2.pl from foo1.pl in Perl then this is what you can do:
print 'hello ';
print qx(perl foo2.pl);
qx is another way to run system commands like backticks. Thus perl foo2.pl is run with the output being sent back to your calling perl script.
So here the same using backticks. Also it uses a direct call to script (which is better):
print 'hello ';
print `./foo2.pl`;
And if you are expecting lots of output from the script then its best not to load it all into memory like above two examples. Instead use open like so:
print 'hello ';
open my $foo2, '-|', './foo2.pl';
print <$foo2>;
close $foo2;
And you can wrap this up into one print statement for "hello world" example:
print 'hello ', do {
open my $foo2, '-|', './foo2.pl';
<$foo2>;
};
/I3az/
Using a shell script (for example, a .bat file on Windows) to run various Perl scripts and combine their output is one way to solve the problem. However, I usually find that Perl itself provides a more powerful and flexible environment than shell scripts. To use Perl in this way, one place to start is by learning about the system and exec commands.
For example:
# In hello.pl
print "Hello\n";
# In world.pl
print "World\n";
# In hello_world.pl.
use strict;
use warnings;
system 'perl hello.pl > hello_world.txt';
system 'perl world.pl >> hello_world.txt';
You can use the following code also
file1.pl
use strict;
use warnings;
open (FH,">file") or die "$! can't open";
print FH "WELCOME\n";
file2.pl
use strict;
use warnings;
open (FH,">>file") or die "$! can't open";
print FH "WELCOME2\n";
The file content is
WELCOME
WELCOME2
If you know beforhand that the script you want to execute from inside the other script is also Perl, you should use do EXPR (https://perldoc.perl.org/functions/do.html).
This executes the contents of the file EXPR in the context of the running perl process and saves you from starting new cmd.exe and perl.exe instances.
hello.pl:
print "Hello";
do "world.pl";
wordl.pl:
print "World";