In Perl, how do I handle input as soon as it arrives, instead of waiting for a new line?

I want to run a subcommand with Perl (or port it to a Perl script) and immediately execute the script command, and not wait for a timeout, a newline or a specific block number. For example, suppose I want to surround each piece of input with square brackets. When I run the script as follows:

$ ( echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz) | my_script.pl 

I would like the result to be like this: each line appears five seconds after the previous one:

 [foo] [bar] [baz] 

How to do it?

This works, but really ugly:

 #! /usr/bin/perl -w use strict; use Fcntl; my $flags = ''; fcntl(STDIN, F_GETFL, $flags); $flags |= O_NONBLOCK; fcntl(STDIN, F_SETFL, $flags); my $rin = ''; vec($rin,fileno(STDIN),1) = 1; my $rout; while (1) { select($rout=$rin, undef, undef, undef); last if eof(); my $buffer = ''; while (my $c = getc()) { $buffer .= $c; } print "[$buffer]\n"; } 

Is there a more elegant way to do this?

+6
perl
source share
5 answers

From perlfaq5: How can I read one character from a file? From the keyboard? . You will probably also want to read How do I know if there is a character waiting on a file descriptor? . Interrogate a file descriptor. If there is a symbol, read it and reset the timer. If there is no character there, try again. If you try again and pass a certain time, process the input.

After you read the characters, you decide what to do with them. With all the flexibility of reading individual characters, additional work arises on their processing.

+9
source share

Term :: ReadKey can do it for you. In particular, setting ReadKey () mode for the survey is for you.

 use Term::ReadKey; $| = 1; while( my $key = ReadKey(10) ) { print $key; } 
+4
source share

If there is time between each character, you can detect pauses.

Perl also performs line input - if you are not using getc, you should be able to add new lines to the end of foo, bar, etc., and perl will provide you with each line.

If you cannot add new lines, and you cannot depend on a pause, then what exactly do you expect from the system to tell perl that it launched a new command? As for perl, that is, a stdin pipe, it feeds on the data from it, and there is nothing in the stdin pipe to tell you when you are executing a new command.

Instead, you might consider the following:

 $ echo "( echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz)" | my_script.pl 

or

 $ my_script.pl$ "echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz" 

And modify your perl program to parse the input "command line" and complete each task, using standard output as needed.

-Adam

+1
source share

You did not specify how you read the input in your Perl script, but you can look at getc :

 $|++; # set autoflush on output while ($c = getc(STDIN)) { print $c; } 
0
source share

See How to change Open2 input buffering . (Basically, you should make another program think that it is talking to tty.)

0
source share

All Articles