http://qs1969.pair.com?node_id=671047

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

Dear monks,

I need to launch a separate executable and record its process ID (on a linux cluster) so I can kill it later on, if need be. The way I've been doing this is by using open:
my $handle = IO::Handle->new; my $pid = open($handle, 'command & |') or die $!;
The problem is that sometimes 'command' spits out a lot of stdout messages, causing a broken pipe on $handle. I'm not really interested in those stdout messages anyway ('command' writes an output file that I'll parse instead) so the only reason I'm using 'open()' is that it gives me the $pid, which 'system()' and 'exec()' don't do. Is there a better way to get the $pid? Without the broken pipe?

Thank you.

Replies are listed 'Best First'.
Re: Launch process, get PID
by pc88mxer (Vicar) on Feb 28, 2008 at 23:26 UTC
    You're executing command in the background, so the pid you are getting back is the pid of the shell that's spawning command and not of command itself. Just want to make sure you're aware of that.

    If you just want to launch command and not worry about it, just use fork and exec directly:

    my $pid = fork(); die "unable to fork: $!" unless defined($pid); if (!$pid) { # child exec('command...'); die "unable to exec: $!"; } # parent continues here, pid of child is in $pid

    In the exec, perhaps you'll want to redirect the child's output to either /dev/null or a file.

    Another option is to 'daemonize' the child which involves detaching it from the parent's process group. For an example, have a look at http://snippets.dzone.com/posts/show/359, and I'm sure there's code on CPAN to do it, too.

    In your original code, I think you were getting broken pipes because you were closing $handle and the child was still writing to its STDOUT.

      Thanks people, that's the ticket. I've seen the idiom before, but couldn't come up with it on my own.
Re: Launch process, get PID
by mr_mischief (Monsignor) on Feb 29, 2008 at 00:32 UTC
    What you want is to explicitly fork() then exec(). That's the tried-and-true traditional way to launch another process on Unixy systems, and fork() returns the child's PID in the parent (and 0 in the child, or undef if the fork() failed).

    This code is simple, but it shows the concept.

    if ( my $pid = fork() ) { my $pid_of_child_that_finished = wait(); my $status_code_of_child = $?; # or $CHILD_ERROR_NATIVE } elsif ( defined $pid ) { exec( '/bin/ls' ); } else { warn "Something broke: can't fork()!\n"; } # continue processing in the parent after child is done
Re: Launch process, get PID
by runrig (Abbot) on Feb 28, 2008 at 23:23 UTC
    See $? in perlvar(Update: sorry, not what you're looking for...I was looking for the Korn Shell version of $!, but it doesn't seem to exist in perl...use fork/exec as suggested below). And if you don't want STDOUT, redirect it to /dev/null.
Re: Launch process, get PID
by dcparham (Initiate) on Dec 02, 2014 at 03:41 UTC
    1-please tell me if i am not clear, or i if am otherwise frustrating as i ask questions - i do not want to bite the hand that feeds me! 2-i have perl code to run a remote command like this focus on $remoteCmd:
    #!/usr/bin/perl -w use Net::SSH::Perl; use Net::SSH::Expect; my $remoteCmd = `ssh myserverid.company.com -l myuid -i /home/myuid/.s +sh/authorized_keys top`;
    As you can see, i try to start "top" - which errs with "TERM environment variable not set." - but even if i were able to start a service using the $remoteCmd = `ssh... cmd, whatever service i start, i need to capture the pid # of the service i just started. i tried your code like this, which did produce a temporary pid:
    #!/usr/bin/perl -w use IO::Handle; my $handle = IO::Handle->new; my $pid = open($handle, 'command & |') or die $!; print $pid;
    ... and that pid prints; so whatever that did, it produced a pid; but i want is to start my own service or process, then capture that pid. then i will check the process table to see if the pid name, and pid # exist. if so, the process is running. if not, i need to restart it. i hope i am clear. basically, i need to ssh to a remote server, start a process, then capture the pid # back to the local perl script that calls the ssh. any ideas? can you help? again, if i am not clear, i will retool my presentation again and strive for even more clarity. thanks!