SPUG: shell script event loops + wrapping shell utilities

Parr, Ryan Ryan.Parr at wwireless.com
Mon Apr 22 03:01:58 CDT 2002


I know this may not be the answer you're looking for, but why aren't you
just using Net::Telnet and Net::FTP? The inconvenience of these mods seems
to be far less than the inconvenience of your current situation.

...

The Camel Book 3rd Edition states on page 431 regarding IPC::Open2 and
IPC::Open3:
"Even simple, apparently interactive programs like ftp fail here because
they won't do line buffering on a pipe. The'll only do it on a tty device.
The IO::Pty and Expect modules from CPAN can help out with this because they
provide a real tty (actually, a real pseudo-tty, but it acts like a real
one). This gets you line buffering in the other process without modifying
its program."

The same page also recommends using the IO::Select module to avoid your
issue with the fine grained sleep. However using the Time::HiRes module you
can just do the following:

use Time::HiRes qw(usleep);
my $microseconds = 100_000; # 0.1 seconds

usleep($microseconds);


-- Ryan Parr

Common sense is the collection of prejudices acquired by age eighteen.
		-- Albert Einstein


-----Original Message-----
From: dancerboy [mailto:dancerboy at strangelight.com] 
Sent: Thursday, April 18, 2002 11:42 PM
To: Seattle Perl Users Group
Subject: SPUG: shell script event loops + wrapping shell utilities


So, I wanted to add some minor functionality to a couple of shell 
programs (telnet and ftp, specifically) and the simplest way seemed 
to be simply to wrap them in a Perl script, i/e simply make a Perl 
script that opens the program, and pipes input from the terminal 
(keystrokes) through to the program, and passes characters output 
from the program back to the terminal -- occasionally adding some 
extra keystrokes of its own (the added functionality) but mostly just 
acting as a "bidirectional pipe" between the program and the terminal.

Here is the main wrapper part of the ftp version of the script 
(greatly stripped-down for clarity):

____________________________
#!/usr/bin/perl

use strict;
use Fcntl;
use IPC::Open3;

$| = 1;

my $from_term;
my $from_shell;
my $pipe_error;

open(TTY, "+</dev/tty") or die "no tty: $!";
fcntl( TTY, F_SETFL, O_NONBLOCK );

my $pid = open3( \*TO_SHELL, \*FROM_SHELL, \*SHELL_ERR,
         'ftp -v strangelight.com'
);

fcntl( FROM_SHELL, F_SETFL, O_NONBLOCK );
fcntl( SHELL_ERR, F_SETFL, O_NONBLOCK );

my $oldfh = select(FROM_SHELL); $| = 1; select($oldfh);
$oldfh = select(SHELL_ERR); $| = 1; select($oldfh);
$oldfh = select(TTY); $| = 1; select($oldfh);

$SIG{PIPE} = sub { ++$pipe_error; };

while ( not $pipe_error ) {
                 while( defined( $from_term = getc(TTY) ) ) {
                         print TO_SHELL $from_term;
                 }
                 while( defined( $from_shell = getc(FROM_SHELL) ) ) {
                         print $from_shell;
                 }
                 while( defined( $from_shell = getc(SHELL_ERR) ) ) {
                         print $from_shell;
                 }
}

__END__

Now, I have two questions.

My first question is, admittedly, one of those "I could probably 
figure it out on my own but I'm lazy so I'll ask the folks on SPUG 
instead" questions:

While my script works fairly well as-is, it's a real processor hog, 
as you can probably guess.  All of those getc() calls are 
non-blocking, so even when there's no input or output to process, the 
main while() loop still keeps executing over and over again, doing 
absolutely nothing as fast as it possibly can.  What's the 
best/simplest way to tell Perl to "go to sleep, but wake up as soon 
as something interesting happens"?

My second question is one I've banged my head on for a while and 
haven't been able to figure out at all:

Even with setting $|=1 on all the open pipes, I still don't always 
get my I/O flushed promptly -- in particular, the responses I read 
back from FROM_SHELL seem often to be one or two lines behind what I 
should be getting. (My work-around has been to send a no-op command 
like 'pwd', the response to which usually forces the lines that I 
want to see out of the buffer.)  What else can I do to get my pipes 
"piping hot" (as the perldocs say)?

-jason

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     POST TO: spug-list at pm.org       PROBLEMS: owner-spug-list at pm.org
      Subscriptions; Email to majordomo at pm.org:  ACTION  LIST  EMAIL
  Replace ACTION by subscribe or unsubscribe, EMAIL by your Email-address
For daily traffic, use spug-list for LIST ;  for weekly, spug-list-digest
     Seattle Perl Users Group (SPUG) Home Page: http://seattleperl.org

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     POST TO: spug-list at pm.org       PROBLEMS: owner-spug-list at pm.org
      Subscriptions; Email to majordomo at pm.org:  ACTION  LIST  EMAIL
  Replace ACTION by subscribe or unsubscribe, EMAIL by your Email-address
 For daily traffic, use spug-list for LIST ;  for weekly, spug-list-digest
     Seattle Perl Users Group (SPUG) Home Page: http://seattleperl.org




More information about the spug-list mailing list