tags:

views:

118

answers:

5

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.

+2  A: 

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.";
}
WhirlWind
the program does exist. The problem is the simple doesn't compile but the message says something else.
mmccoo
nix that. I'm and idiot
mmccoo
@mmc: you can delete comments that are invalid: click on the red x that appears to the right of your name and timestamp.
Ether
+5  A: 

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 :)

Igor Krivokon
+1. Also I find it amusing to think that the programming language is responsible for giving user-friendly error messages.
tster
simple.pl does exist. The problem is that it doesn't compile. The message I get doesn't say it can't compile but rather that the program doesn't exist. misleading.Good catch. simple2 should call simple
mmccoo
nix my comment. I'm an idiot
mmccoo
If I change the first path to "./simple.pl" I get: Can't exec "./simple.pl": Permission denied at simple2.pl line 4. The second still gives me Can't exec "simple.pl": No such file or directory at simple2.pl line 6. Igor is right. Even though simple.pl exists in the current working directory, it does not exist **on the path**.
David Dorward
+1  A: 

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';

  # ...
}
Greg Bacon
A: 

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.

Eric Strom
A: 

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).

Sid Burn