Reputation: 1375
perl -e 'system ("crontab1 -l");print $?'
returns -1 as expected (program crontab1 doesn't exist)
perl -e 'system ("crontab1 -l|grep blah");print $?'
returns 256.
What is the way to check the status of the first (or both) programs?
Upvotes: 3
Views: 1973
Reputation: 9301
Remember, you're supposed to use $?>>8 to get the exit code, not $?
perl -e 'system("set -o pipefail;false | true");print $?>>8,"\n"'
1
This ("pipefail") only works if your shell is bash 3. Cygwin and some Linux flavors ship with it; not sure about mac.
You should be aware that 256 is an error return. 0 is what you'd get on success:
perl -e 'system("true");print $?>>8,"\n"'
0
I didn't know system returned -1 for a single command that isn't found, but $?>>8 should still be non-zero in that case.
Upvotes: 3
Reputation: 386501
[This was composed as an answer to another question which was closed as a duplicate of this one.]
Executing a shell command requires executing a shell. To that end,
system($shell_command)
is equivalent to
system('/bin/sh', '-c', $shell_command)
As such, all of your examples run a single program (/bin/sh
). If you want the exit statuses of multiple children, you're going to need to have multiple children!
use IPC::Open3 qw( open3 );
open(local *CHILD1_STDIN, '<', '/dev/null')
or die $!;
pipe(local *CHILD2_STDIN, local *CHILD1_STDOUT)
or die $!;
my $child1_pid = open3(
'<&CHILD1_STDIN',
'>&CHILD1_STDOUT',
'>&STDERR',
'prog1', 'arg1', 'arg2',
);
my $child2_pid = open3(
'<&CHILD2_STDIN',
'>&STDOUT',
'>&STDERR',
'prog2', 'arg1', 'arg2',
);
my @pipe_status = map { waitpid($_, 0) } $child1_pid, $child2_pid;
IPC::Open3 is rather low level. IPC::Run3 and/or IPC::Run can possibly make this easier. [Update: Indeed, IPC::Run does].
Upvotes: 2
Reputation: 12044
You are getting the exit status of the entire command, just as you should. If you want the exit status separately, you're going to have to run the commands separately.
#!/usr/bin/perl -e
system("crontab1 -l > /tmp/junk.txt"); print $?;
system("grep blah /tmp/junk.txt"); print $?;
as an example.
Upvotes: 4
Reputation: 58681
What is the way to check the status of the first (or both) programs?
There is no such way, at least, not as you have constructed things. You may have to manage sub-processes yourself via fork(), exec() and waitpid() if you must know these things.
Here is roughly what is happening in your code fragment.
perl
's system() spawns a shell, and perl
*wait()*s for that subprocess to terminate.
The shell sets up a pipeline:
grep
on the read-end of the pipecrontab1
anywhere in $PATH
, and *exit()*s 127 (on my system, that is, where 127 is the shell indicating failure to find a program to run).grep
detects end-of-file on its input and, having matched nothing, *exit()*s 1.
The shell *exit()*s with the exit code of the last process in the pipeline, which, again, is 1.
perl
detects the shell's exit code of 1, which is encoded in $?
as 256.
(256 >> 8 == 1)
Upvotes: 1
Reputation: 42154
If you tolerate using something other than system
, there are easier solutions. For example, the results
method in IPC::Run returns all exit codes of the chain.
Upvotes: 4
Reputation: 46225
The operating system returns an exit status only for the last program executed and if the OS doesn't return it, perl can't report it.
I don't know of any way to get at the exit code returned by earlier programs in a pipeline other than by running each individually and using temporary files instead of pipes.
Upvotes: 1
Reputation: 132896
If you want to check the status, don't put them all in the same system. Open a reading pipe to the first program to get its output then open another pipe to the other program.
Upvotes: 1