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

Hello, Monks.
My first query here. I'm excited.

We have a number of machines (Slackware Linux, Perl 5.10) acting as data collectors: each has some number of serial inputs, each serial port writes to a file as stuff (ASCII text) arrives. There is a requirement for real-time viewing of incoming data -- the serial port data files. We came up with an application using Perl/Tk, but in the interest of minimizing our time rebuilding OS's we would rather stay away from X windows and instead have a simple curses application which would ordinarily be the only thing the data viewer user would see. The problem: using Curses::UI::TextViewer, no data ever displays.

To read the data file we discovered a number of sources which all recommended this method:

$errcode = open(FH, " tail -f $filename | ");

What none of these sources mentioned is that each time one of these filehandles is closed, an orphaned tail PID is left hanging out there. We came up with a little package for opening and closing the files which also tracks the tail PID's and disposes of them appropriately. The Perl/Tk code looks like this:

#!/usr/bin/perl use strict; use warnings; use Tk; require "./SFDTail.pm"; . . # SFDTail.pm is our file handling package my $tailer = SFDTail::new(); my $mw = MainWindow->new(); my $txtarea = $mw->Scrolled('Text', ... option stuff ...); . . MainLoop; . . # grabtext() is our Tk::fileevent callback function # $tailer->fh() returns a file handle ($fh), created as: # open($fh, "tail -f -n 20 $self->{file} |"); sub grabtext { # $stuff = <$tailer->fh()> don't work --?? my $glob = $tailer->fh(); my $stuff = <$glob>; $txtarea->insert('end', $stuff); $txtarea->yview('end'); } . . # Tk::Button event callback: select appropriate file, let $tailer ope +n it. # $tailer->tail($file) is where the file gets opened, as above. sub standard { my $unit = shift; # typically 'Unit01', 'Unit02', etc # clean up: close any previous filehandle and kill any tail PID's $tailer->closepipe(); my $file = "/usr/local/scripts/tk/${unit}.txt"; $txtarea->delete('1.0', 'end'); # if data input not active, the file may not be there if ( -f $file ) { if ( $tailer->tail($file) ) { $mw->fileevent($tailer->fh(), 'readable' => \&grabtext); } } else { $txtarea->insert('end', "\n No data for $unit. \n"); } }

The Perl/Tk thing works great, but trying to duplicate that in Curses:UI is not going so well. We're guessing that it's a blocking thing which somewhere between Tk::fileevent and Tk::Mainloop just gets quietly handled for us. We so far have been unable to get Curses::UI to go about its business and also display the results of the piped-tail filehandle. We would be most grateful for any suggestions, and thanks in advance.


< five days later >
Tried the select loop, but I guess I'm too dense to figure out how to get it to behave the way I needed it to. Finally found a workable solution using Curses::UI::POE and POE::Wheel::FollowTail. Thanks everybody for your consideration.

Replies are listed 'Best First'.
Re: Blocking filehandle (?)
by ikegami (Patriarch) on Aug 24, 2010 at 15:32 UTC

    What none of these sources mentioned is that each time one of these filehandles is closed, an orphaned tail PID is left hanging out there.

    I don't think so. As per the documentation and confirmed by experimentation, if you close the file handle, it blocks until the child exits.

    The problem is that it could block for a long time — it tail doesn't have anything to write, it won't notice the pipe is closed — unless you kill tail using a signal first.

      Sorry ikegami, but my tail PIDs are still there after the filehandle is closed, even after the script successfully exits, every time. But that's not the problem. The problem is blocking (or something) while the filehandle is open -- and nothing shows on the curses display.

Re: Blocking filehandle (?)
by JavaFan (Canon) on Aug 24, 2010 at 14:49 UTC
    Uhm, why not use a classical select loop?