vt.
vt.

Reputation: 1375

How can I check the status of the first program in pipeline in Perl's system()?

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

Answers (7)

Jonathan Graehl
Jonathan Graehl

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

ikegami
ikegami

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

Michael Kohne
Michael Kohne

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

pilcrow
pilcrow

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.

  1. perl's system() spawns a shell, and perl *wait()*s for that subprocess to terminate.

  2. The shell sets up a pipeline:

    1. a subshell *exec()*s grep on the read-end of the pipe
    2. a subshell fails to locate crontab1 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).
  3. grep detects end-of-file on its input and, having matched nothing, *exit()*s 1.

  4. The shell *exit()*s with the exit code of the last process in the pipeline, which, again, is 1.

  5. perl detects the shell's exit code of 1, which is encoded in $? as 256.
    (256 >> 8 == 1)

Upvotes: 1

JB.
JB.

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

Dave Sherohman
Dave Sherohman

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

brian d foy
brian d foy

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

Related Questions