Executing Nonblocking System Commands
One of the most common requests seen on
the comp.lang.perl.tk newsgroup is how to
execute a system command and display its output in a Text widget. The
typical response is some variation of tktail,
which uses fileevent
to signal that output data is
available without blocking the application.
Here’s the program:
open(H, "tail -f -n 25 $ARGV[0]|") or die "Nope: $!"; my $t = $mw->Text(-width => 80, -height => 25, -wrap => 'none'); $t->pack(-expand => 1); $mw->fileevent(\*H, 'readable', [\&fill_text_widget, $t]); MainLoop; sub fill_text_widget { my($widget) = @_; $_ = <H>; $widget->insert('end', $_); $widget->yview('end'); }
The
standard way to keep Perl/Tk programs from blocking is to use
multiple processes. Here we use Perl’s
open
function to create a separate process that
sends its output to a pipe. fileevent
then defines
a callback that gets invoked whenever the file handle
H
has data available to read. The callback appends
one line to the Text widget and uses yview
to
ensure that we always see the end of the file.
There’s a problem here. The statement $_
= <H>
expects to read an
entire line, one that’s newline terminated. If only a partial
line were available, the read would block, and so would
tktail. To be rigorous, we should use
sysread
for our I/O, which handles partial lines:
sub fill_text_widget { my($widget) = @_;my($stat, $data);
$stat = sysread H, $data, 4096;
die "sysread error: $!" unless defined $stat;
$widget->insert('end', ...
Get Mastering Perl/Tk now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.