SPUG: shell script event loops + wrapping shell utilities
Doug Beaver
doug at beaver.net
Fri Apr 19 19:25:37 CDT 2002
On Thu, Apr 18, 2002 at 11:42:24PM -0700, dancerboy wrote:
> #!/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"?
You want to use either select() or IO::Select. Check out your system
manpage for select(2) to get a general idea of what it does and then
check out the perldoc -f for select, it has an example of selecting on
multiple filehandles. If it looks too daunting, you could use
IO::Select's OO api too. select has a timeout option, so you can make
it block forever or timeout after a given number of seconds. I bet the
perl cookbook even has an example or two of select on filehandles, it's
a common idiom. I don't have a copy handy, otherwise I would give you a
pointer. The perldoc says you shouldn't mix select with buffered i/o,
so maybe you want to use select & sysread instead.
> 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)?
Sorry, I don't know about this. Maybe it has something to do with
buffered i/o. You could try using sysread instead of <> for the i/o and
see if that solves your problem. That combined with the select just
might do the trick... Just be careful not to mix sysread with buffered
reads (like read() or <>).
Doug
--
Space Ghost: Moltar, I have a giant brain that is able to reduce any
complex machine into a simple yes or no answer.
Moltar: Okay, but that's not the cd burner...
Space Ghost: Moltar! <pause> Yes!
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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