Re^2: Getting a Perl program to close a command it starts when the perl program exits?
by buzzthebuzzsaw (Acolyte) on Aug 29, 2007 at 16:56 UTC
|
| [reply] |
|
|
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.
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
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.
| [reply] [d/l] [select] |
Re^2: Getting a Perl program to close a command it starts when the perl program exits?
by kyle (Abbot) on Aug 29, 2007 at 16:58 UTC
|
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? | [reply] [d/l] |
|
|
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.
| [reply] [d/l] [select] |
|
|
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.
OK, I think I understand now.
The difference is that when you open the lexical, there's an (IO::Handle) object implicitly created. That object gets a call to DESTROY before the END block is called. The DESTROY method calls close, which has to wait for the subprocess to finish, which it never does.
In the case of a global file handle, Perl itself will close it after the END block is called. In that case, END can kill the process and everything else goes fine.
package Foo;
sub new { bless {} }
sub DESTROY { print "DESTROY()\n" }
package main;
my $foo = Foo->new();
print "I have a foo.\n";
END { print "END()\n" }
exit;
__END__
I have a foo.
DESTROY()
END()
| [reply] [d/l] [select] |
|
|
|
|
| [reply] |
|
|
|
|
| [reply] |