Raiden690 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Perl Monks, i would like to get the exact same behaviour that i get with $ret = system( "some command"); but in addition i want to get the pid of that command, i been reading about the differences between exec,system and backticks, as i understand none of these is usefull for what i want to do. what worked for me was this:

>...my $ret = undef; >...my $pid = undef; >...if( $pid = fork()){ >...>...#here i store the pid in some array for other process >...} >...elsif( defined $pid){ >...>...$ret = system($cmd); >...>...open(my $f,">","/tmp/$$") or die "couldn't write file"; >...>...print $f $ret; >...>...close($f); >...>...die;#not sure if this is the proper way to kill the child, not + my main worry in this question >...} >...else{ >...>...print "\n couldnīt fork\n"; >...} >...waitpid($pid,0); >...if( open(my $F, "/tmp/$pid")){ >...>...while( my $line = <$F>){ >...>...>...$ret .= $line; >...>...} >...>...close($F); >...>...system("rm /tmp/$pid"); >...} >...else{ >...>...$ret = -1; >...}

i know this is suuper awfull, so mi question is: how i do this properly? p.s: sorry my english, and be good, this is my first question here.

p.s 2: i think the get_pidof function wont do because there will be several identical commands running at the same time.

Replies are listed 'Best First'.
Re: get same return that system gives and get pid
by haukex (Archbishop) on Aug 10, 2016 at 19:03 UTC

    Hi Raiden690,

    Could you try and paste your code without what I'm guessing is your editor's whitespace markers? (See How do I change/delete my post?)

    i would like to get the exact same behaviour that i get with $ret = system( "some command"); but in addition i want to get the pid

    When system returns, the external command would normally have finished, so what good will the PID do you? (Windows has the system(1, @args) variant, but I'm guessing you're not on Windows.) Could you explain the bigger picture of what you're trying to accomplish?

    In case you're writing some kind of process/daemon that's supposed to go into the background and keep running (and manage a PID file), there are several modules to help you. For example, I've used Daemon::Control a lot recently, although that comes with a lot of extra functions for writing an LSB compatible daemon. There are plenty of alternatives, see for example the recent thread process run in background.

    Hope this helps,
    -- Hauke D

Re: get same return that system gives and get pid
by FreeBeerReekingMonk (Deacon) on Aug 11, 2016 at 13:53 UTC
    An alternative (best is to use modules, it saves you from deadlock pitfalls) is using pipes:

    perlipc#Safe-Pipe-Opens

    The problem with pipes is that you can kill your child, or the child hangs or takes too long, without the parent knowing, as it is "reading". So you need an alarm ($SIG{'ALRM'}) in your parent. A way I do it (so the parent stays responsive)

    #!/usr/bin/perl use strict; use warnings; my $parent_pid = $$; my $child_pid = 0; my @child_output; my $read_from_child = 0; my $max_timeout = 60; my $timeout; print "Parent is $parent_pid\n"; my $pid = open(KID_TO_READ, "-|"); defined($pid) || die "can't fork: $!"; #local $SIG{CHLD}; if ($pid) { # parent local $SIG{'CHLD'} = sub { print "dead child\n"; $timeout=0 }; local $SIG{'USR1'} = sub { ++$read_from_child }; print "reading kid...\n"; $timeout = $max_timeout; while($timeout >0){ print "parent $parent_pid timeout=$timeout read_from_child=$re +ad_from_child - child_pid=$child_pid\n"; if($read_from_child>0){ --$read_from_child; $_=<KID_TO_READ>; print "parent $$ just read: $_"; unless( $child_pid>0 ){ chomp($child_pid = $_); }else{ push @child_output, $_; } }else{ sleep 1; --$timeout; } } close(KID_TO_READ); } else { # child print $$ . "\n"; # send child pid kill('USR1', $parent_pid); # warn parent there is data print STDERR "child $$ is running\n"; # print to screen (DEBUG) my $program = "/bin/sleep"; my @args = qw(3); my $ret = system($program, @args); print "returncode is $ret\n"; # is $? print STDERR "returncode is $ret\n"; # print to screen (DEBUG) kill('USR1', $parent_pid); # warn parent there is data exit 0; } print "\nChild $child_pid is done, we now have:\n@child_output\n";

    which prints something like:

    Parent is 5323 reading kid... parent 5323 timeout=60 read_from_child=0 - child_pid=0 child 5324 is running parent 5323 timeout=59 read_from_child=1 - child_pid=0 parent 5323 just read: 5324 parent 5323 timeout=59 read_from_child=0 - child_pid=5324 parent 5323 timeout=58 read_from_child=0 - child_pid=5324 parent 5323 timeout=57 read_from_child=0 - child_pid=5324 parent 5323 timeout=56 read_from_child=0 - child_pid=5324 returncode is 0 parent 5323 timeout=55 read_from_child=1 - child_pid=5324 parent 5323 just read: returncode is 0 parent 5323 timeout=55 read_from_child=0 - child_pid=5324 dead child Child 5324 is done, we now have: returncode is 0

    See also:

    Re: How to make parent wait for all the child processes.

      Note that you should never print inside the subroutines of signals, thus, for production code:

      BAD:

      local $SIG{'CHLD'} = sub { print "dead child\n"; $timeout=0 };

      GOOD:

      local $SIG{'CHLD'} = sub { $timeout=0 };

      Also, in the above code, $pid == $child_pid (only $child_pid is send from the child to the parent in a IPC)

      So if you look at the code from perlplexer in

      Perl IPC: Checking Exit Status of Child Process

      This is something you actually want:

      #!/usr/bin/perl use strict; use warnings; my $timeout = 10; my $parent_pid = $$; my $child_pid = fork(); die "Can't fork() : $!\n" unless defined $child_pid; if ($child_pid){ # parent eval { local $SIG{ALRM} = sub { die "timed-out\n" }; alarm $timeout; if (waitpid($child_pid, 0) > 0){ my ($rc, $sig, $core) = ($? >> 8, $? & 127, $? & 128); if ($core){ print "$child_pid dumped core\n"; }elsif($sig == 9){ print "$child_pid was murdered!\n"; }else{ print "$child_pid returned $rc"; print ($sig?" after receiving signal $sig":"\n"); } }else{ print "$child_pid... um... disappeared...\n"; } alarm 0; }; if($@ eq "timed-out\n"){ print "$child_pid... parent got bored waiting for it...\n"; } }else{ # child print "child $$ is running...\n"; my $ret = system("sleep 20"); exit $ret; }

      edit: added alarm

      edit2: After reading more about what you actually are trying to do, you also need to loop over the waitpid in such a way that it returns the pid of the child that is ready. Implementing multi queued children is a bit harder. And would require more details about what you require, as, for example, you can prestart 10 children, that are idle until some work for them is done, or you have a single activity queue and a maximum of X children to do the work, and spawn them on the fly when they need to do something.

      Checking which child is ready to give you their exitcode can be done with WNOHANG, see waitpid

      Depending on what you want to do, independant threads are also an option: threads