SPUG: shell script event loops + wrapping shell utilities
dancerboy
dancerboy at strangelight.com
Fri Apr 19 01:42:24 CDT 2002
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
More information about the spug-list
mailing list