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

Hello All, I am attempting to allow a perl-Tk program to receive commands from some other program while it is doing its thing. From reading 'Mastering perl Tk' I thought the solution would be to use the fileevent method...
my $fh = FileHandle->new("> program_ipc_file"); $fh->close; if (-f 'program_ipc_file') { $fh = FileHandle->new("tail -f -n 25 program_ipc_file|"); $widget->fileevent($fh, 'readable', sub {$me->_process_fileevent +});
which works fine except that the program will not terminate cleanly. I have to ctrl-c it and some times the tail process is left running. Is there something I can do to close the program properly or is there a cleaner way to perform the IPC (of messaging) required? Thanks for the help Brian Miller

Replies are listed 'Best First'.
Re: Perl non-blocking IPC
by zentara (Cardinal) on Jul 06, 2011 at 15:07 UTC
    Hi, I'm not clear what you mean to receive commands from some other program while it is doing its thing? There may be a better way than tailing the output of the program and processing it for commands. Something like IPC::Open3 might be better than tailing, but it all depends on your program.

    Anyways, with the lack of details, I think you probably need to kill the $pid of program_ipc, otherwise post details of what this program outputs, and how it operates.

    In the meantime, try something like the following, and remember, you may need to use Proc::Killfam instead of a plain kill, depending on whether you get the pid of any shell running the program.

    #!/usr/bin/perl # tktail pathname use warnings; use strict; use Tk; my $pid = open(H, "tail -f -n 25 $ARGV[0]|") or die ; my $mw = MainWindow->new; my $t = $mw->Text(-width => 80, -height => 25, -wrap => 'none'); $t->pack(-expand => 1); $mw->fileevent('H', 'readable', [\&fill_text_widget, $t]); $mw->OnDestroy(\&quitCB); MainLoop; sub fill_text_widget { my($widget) = @_; my $text = <H>; $widget->insert('end', $text); $widget->yview('end'); } # end fill_text_widget sub quitCB { kill 9,$pid or die $!; exit; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      Thanks for the responses.

      By 'receive commands', I just mean that some other program writes a text message to the file that the tk-program is looking at (through the fileevent method). The tk-program then gets a call to the _process_fileevent method, reads the file handle and does whatever it says.

      I am attempting to use the 'kill 9' command from your code however I and using the FileHandle module to open the pipe so I don't now how to get the pid of the tail process. Any suggestions?

      $fh = FileHandle->new("tail -f -n 25 command_file |");

      Thanks again Brian

        I don't now how to get the pid of the tail process. Any suggestions?

        $fh = FileHandle->new("tail -f -n 25 command_file |");

        The only way to get the $pid is to use a piped-open as described in perldoc perlipc, or use something like IPC::Open3, but you sometimes get a shell pid, which then executes your program.

        So use something like this:

        my $fh = new FileHandle; my $pid = open( $fh , "tail -f -n 25 command_file | ") or die ; #..... # now you can check the output of ps to see if you get a shell $pid # or just your process $pid # then to make sure you kill all shells and commands, if you see # the $pid is the shell use Proc::Killfam; $mw->protocol('WM_DELETE_WINDOW' => sub { killfam 9,$pid; exit; }); # or put the killfam in the callback of an Exit button, etc
        Before I get slapped on the wrist for using killfam 9, remember that 9 is an instant kill, you may want to use a softer signal like 15, to allow the program to gracefully close.

        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
        It's been a while since I've seen actual FileHandle usage. You don't need it; assuming you're using a perl newer than 5.00505:

        open(my $fh, '-|', qw( tail -f -n command_file)) || die("Unable to run tail: $!.\n");

        You can pass a single string, but whenever possible, call system(), open(), etc. with a list for the command; it's safer, because you're guaranteed not to get filtered through /bin/sh.

Re: Perl non-blocking IPC
by glasswalk3r (Friar) on Jul 06, 2011 at 15:24 UTC

    Anyway how the program being called and how it executed, yo will need to manager if you're invoking it from the Tk program. You had not told in which operational system you're running that, but for UNIX like systems you should look at the functions fork, open, waitpid and POSIX signals.

    If you have control of the program being executed (you can modify it's source code), this will be even easier.

    Regarding the See IO::Select to try to use non-blocking reading.

    Alceu Rodrigues de Freitas Junior
    ---------------------------------
    "You have enemies? Good. That means you've stood up for something, sometime in your life." - Sir Winston Churchill
      Thanks for the response,

      The OS is CentOs Linux. The program that is sending the command to the Tk-program is external and unrelated.

      Are you suggesting that the fileevent method is not the best way to listen and process external commands in a non-blocking way?

      Thanks Brian