3
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?

brian d foy
  • 129,424
  • 31
  • 207
  • 592
vt.
  • 1,325
  • 12
  • 27

7 Answers7

4

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.

Michael Kohne
  • 11,888
  • 3
  • 47
  • 79
  • @Michael Kohne, by "output" do you mean "termination status"? The OP doesn't want to differentiate his subprocesses' *output*, but their exit statuses. – pilcrow Aug 26 '09 at 03:06
  • This is helpful, but now I would need an error handling for a file access. For example this: perl -e 'system("crontab1 -l > /blah"); print $?;' still gives me 256. I guess I would need to split it further :) – vt. Aug 26 '09 at 14:04
  • 1
    No need for a temp file. JB has a better answer below. – runrig Aug 26 '09 at 15:08
4

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.

JB.
  • 40,344
  • 12
  • 79
  • 106
  • I'm sure IPC::Run is great, but back when I was looking for something similar I found Proc::SafeExec first. It worked great, though seems a little more verbose then IPC::Run. It's nice to have options :-) – runrig Aug 26 '09 at 14:57
  • Good info, thanks, but IPC is currently not a part of the project, prefer not to introduce new dependency at this time. – vt. Aug 26 '09 at 17:35
  • 1
    @vt: you can accomplish something similar with open(..., "-|", ...) or open(..., "|-", ...) (fork with STDIN/STDOUT attached between child processes), and exec(...), but IPC::Run or Proc::SafeExec is so much easier to use. I'd get over your preference if I were you, rather than using a temp file (which is another type of dependency). I have some code here: perlmonks.org/?node_id=246397 – runrig Aug 27 '09 at 17:03
3

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.

terdon
  • 3,260
  • 5
  • 33
  • 57
Jonathan Graehl
  • 9,182
  • 36
  • 40
2

[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].

Community
  • 1
  • 1
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

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.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
1

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.

Dave Sherohman
  • 45,363
  • 14
  • 64
  • 102
1

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)

pilcrow
  • 56,591
  • 13
  • 94
  • 135