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

Hi All,

I have a perl program that uses the following to get information from another process:

$tail_id = open(GET_INFO, "tail -1lf info_file 2>/dev/null|") || die " +Can't check info_file: $!\n"; $mw->fileevent(\*GET_INFO, 'readable' => [\&input_from_tail, $mw, info +_file]);

When the perl program finishes the tail command is left running away and has to be killed manually.

Is there a way to end the tail command when the perl program finishes?

I've tried using a $SIG{'QUIT'} and $SIG{'TERM'} to get them to close the pipe to the tail command but they don't appear to work. When the user exits the program nothing happens.

Any ideas?

  • Comment on Getting a Perl program to close a command it starts when the perl program exits?
  • Download Code

Replies are listed 'Best First'.
Re: Getting a Perl program to close a command it starts when the perl program exits?
by BrowserUk (Patriarch) on Aug 29, 2007 at 16:42 UTC
      That's nearly perfect.
      The only thing I'm seeing is that the tail is being kicked off through an sh. The process id of this is what's being returned to $tail_id and not the tail command itself.
      If the tail command is killed it ends the shell.

      Know how to stop the shell getting involved? Or getting the id of the command itself?

        Don't use shell meta-characters (such as >) and the shell won't get involved. Or use IPC::Open3's open3 instead of open '...|'.

        One important difference between open and open3 is that open will call waitpid for you when the file handle is closed (implicitely or explicitely), while open3 won't. You have to call waitpid if you use open3 or you'll end up with zombies.

        I don't use *nix, so this is something I've read about rather than done for myself, but...

        there is mention (in perlfunc open, maybe more in perlopentut?) of a more-than-3-argument form of the piped open. open FILEHANDLE,MODE,EXPR,LIST

        The idea is that you bypass the shell and run the command (tail in this case) directly, and so get the handle of the tail process returned rather than that of the shell. It would look something like:

        my $tail_id = open my $tail_fh, '-|', 'tail', '-1lf', 'info_file' or d +ie ...; END{ kill 3, $tail_id; }

        The caveat is that the shell is what performs the redirection of stderr in your example, so you cannot do that with this form of open.

        If that is a requirement then you are into using IPC::Open3 with all the complexities that entails.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

      I have another question.

      If I modify yours slightly to use a lexical instead of a global file handle, it doesn't work anymore.

      $tail_id = open(my $tail_fh, "tail -1lf /dev/null 2>/dev/null|") || die "Can't check info_file: $!\n"; END{ kill 3, $tail_id; }

      Why does this make a difference?

        The problem is the order in which the handle's destructor and the END block are executed. If the file handle is destroyed first, you have a problem. When destroying a file handle, perl closes it, and closing a file handle opened with open '...|' causes perl to wait until the child process exits. Seeing as the kill command hasn't been executed, that won't happen.

        You don't have that problem with open3 since you control when waitpid is called.

        use IPC::Open3 qw( open3 ); my @tail_pids; END { for my $tail_pid (@tail_pids) { kill TERM => $tail_pid; waitpid($tail_pid, 0); } } sub start_tail { my ($mw, $file) = @_; my ($from_tail, $tail_err); push @tail_pids, open3(undef, $from_tail, $tail_err, qw( tail -1lf +), $file); $mw->fileevent($from_tail, 'readable' => [ \&input_from_tail, $mw, +$file ]); $mw->fileevent($tail_err, 'readable' => [ \&sink, $mw, +$file ]); }

        Notes:

        • Placing END in start_tail won't work.
        • You need a simple sink function that simply reads and discards the data waiting in the file handle.
        • It now handles file names containing special characters.
        • It doesn't handle SIGPIPE from tail ending prematurely.
        • Untested.