Flushing Perl STDIN buffer - perl

Is there any way to clear the STDIN buffer in Perl? A part of my program has lengthy output (enough time for someone to enter a few characters) and after that output I ask for input, but if characters were entered during the output, they are "tacked on" to whatever is entered at the input part. Here is an example of my problem:
for(my $n = 0; $n < 70000; $n++){
print $n . "\n";
}
chomp(my $input = <STDIN>);
print $input . "\n";
The output would include any characters entered during the output from that for loop. How could I either disable STDIN or flush the STDIN buffer (or any other way to not allow extra characters to be inserted into STDIN before calling it)?

It looks like you can accomplish this with the Term::ReadKey module:
#!perl
use strict;
use warnings;
use 5.010;
use Term::ReadKey;
say "I'm starting to sleep...";
ReadMode 2;
sleep(10);
ReadMode 3;
my $key;
while( defined( $key = ReadKey(-1) ) ) {}
ReadMode 0;
say "Enter something:";
chomp( my $input = <STDIN> );
say "You entered '$input'";
Here's what happens:
ReadMode 2 means "put the input mode into regular mode but turn off echo". This means that any keyboard banging that the user does while you're in your computationally-expensive code won't get echoed to the screen. It still gets entered into STDIN's buffer though, so...
ReadMode 3 turns STDIN into cbreak mode, meaning STDIN kind of gets flushed after every keypress. That's why...
while(defined($key = ReadKey(-1))) {} happens. This is flushing out the characters that the user entered during the computationally-expensive code. Then...
ReadMode 0 resets STDIN, and you can read from STDIN as if the user hadn't banged on the keyboard.
When I run this code and bang on the keyboard during the sleep(10), then enter some other text after the prompt, it only prints out the text I typed after the prompt appeared.
Strictly speaking the ReadMode 2 isn't needed, but I put it there so the screen doesn't get cluttered up with text when the user bangs on the keyboard.

I had the same problem and solved it by just discarding anything in STDIN after the processing like this:
for(my $n = 0; $n < 70000; $n++){
print $n . "\n";
}
my $foo=<STDIN>;
print "would you like to continue [y/n]: ";
chomp(my $input = <STDIN>);
print $input . "\n";

{ local $/; <STDIN> }
This temporarily - limited to scope of the block - sets $/, the input record seperator, to be undef, which tells perl to just read everything instead of reading a line at a time. Then reads everything available on STDIN and doesn't do anything with it, thus flushing the buffer.
After that, you can read STDIN as normal.

Related

Perl diamond operator in double while loop hangs

In my Perl script I have a double infinite while loop. I read lines from a file with the diamond operator. But somehow if my script reaches the last line of the file, it does not return undef, but hangs forever.
If I reduced my code to a single while loop this did not happen. So I wonder if I am doing something wrong or if this is a known limitation of the language. (This is actually my first perl script.)
Below is my script. It is meant to count the size of DNA sequences in fasta files, but the hanging behavior can be observed with any other file with multiple lines of text.
Perl version 5.18.2
Invoked from the commandline like perl script.pl file.fa
$l = <>;
while (1) {
$N = 0;
while (1) {
print "Get line";
$l = <>;
print "Got line";
if (not($l)) {
last;
}
if ($l =~ /^>/) {
last;
}
$N += length($l);
}
print $N;
if (not($N)) {
last;
}
}
I put some debug print statements so that you can see that the last line printed is "Get line" and then it hangs.
Welcome to Perl.
The issue with your code is that you have no way of escaping the outer loop. <> will return undef when it reaches the end of the file. At this point your inner loop ends and the outer loop sends it back in. Forcing further reads causes <> to start looking at STDIN which never sends an EOF, so your loop continues forever.
As this is your first Perl script I'm going to rewrite it for you with some comments. Perl is a fantastic language, you can write some great code, however mostly due to it's age there are some older styles which are no longer advised.
use warnings; # Warn about coding errors
use strict; # Enforce good style
use 5.010; # Enable modernish (10 year old) features
# Another option which mostly does the same as above.
# I normally do this, but it does require a non-standard CPAN library
# use Modern::Perl;
# Much better style to have the condition in the while loop
# Much clearer than having an infinite loop with break/last statements
# Also avoid $l as a variable name, it looks too much like $1
my $count = 0; # Note variable declaration, enforced by strict
while(my $line = <>) {
if ($line =~ /^>/) {
# End of input block, output and reset
say $count;
$count = 0;
} else {
$count += length($line);
}
}
# Have reached the end of the input files
say $count;
try "echo | perl script.pl file.fa".
works for me with same "problem" in my code.
gets EOF from stdin.

Read multiple lines from console

How to read multiple lines from console in Perl?
I have used #a = <STDIN>; but I am unable to come out of that statement. Evertime I hit enter it goes to new line. I have read to hit ctrl+d to end the input but it does not seem to work.
Maybe a better idea would be a loop of some sort:
use strict;
use warnings;
my #a;
for(;;) {
my $input = <STDIN>;
last if not defined $input;
chomp $input;
push #a, $input;
}
This will end when you type in the Unix <EOF> (which is usually set to Ctrl-D by default).
You can use while loop,
my #a;
while (<STDIN>) {
/\S/ or last; # last line if empty
push #a, $_;
}
print #a;
It seems like you are on Windows. On Windows you have to hit Control-z on an empty line and then hit Enter.

Why does this perl program not print anything out?

#!/usr/bin/env perl
use Term::ReadKey;
ReadMode 4;
END {
ReadMode 0; # Reset tty mode before exiting
}
while (<>) {
$key = ReadKey(0);
$key == "\x04" and last; # Ctrl+D breaks the loop
print $key;
}
When I had it without the while loop, it was printing back what I typed in.
It doesn't even produce any output at the end (if it was buffering it or something). Like I'd run it and type a few letters and hit Ctrl+D. It prints nothing.
I'm trying to make a program to convert mouse scroll escape codes into keypresses. I hope I'm not barking up the wrong tree.
This line
while (<>)
reads a line from STDIN (assuming you ran the program with no command line arguments). Once a line has been read, it enters the body of the while loop. Whatever you typed up to and including the newline is now in $_.
Now, you press a key, it's stored in $key and numerically compared to CTRL-D. Since neither is numeric, they both end up being zero, the loop terminates.
This is why you should turn on warnings which would have told you:
Argument "^D" isn't numeric in numeric eq (==) at ./tt.pl line 15, line 1.
Argument "c" isn't numeric in numeric eq (==) at ./tt.pl line 15, line 1.
Of course, it would make sense to put the loop-termination condition where it belongs as well:
#!/usr/bin/env perl
use strict;
use warnings;
use Term::ReadKey;
ReadMode 4;
END {
ReadMode 0; # Reset tty mode before exiting
}
my $input;
{
local $| = 1;
while ((my $key = ReadKey(0)) ne "\x04") {
print $key;
$input .= $key;
}
}
print "'$input'\n";
Just replace the while condition to:
while(1) {
# ...
}

Does Perl's $| setting affect system commands?

I am looking at some old code in Perl, where the author has writtern
$| = 1 in the first line.
But the code does not have any print statements, it calls a C++ binary using the system command. Now I read that $| will force flush after every print. So does it affect the system command's output in any way or am I safe to remove that line.
Thanks
Arvind
I do not believe so. The $| will affect the way that Perl is running, not any external executable.
You should be safe to remove it.
perldoc - perlvar : States "If set to nonzero, forces a flush right away and after every write or print on the currently selected output channel.". I think the important thing here is the "currently selected output channel". The external application will have it's own output channel.
With questions like this it is often easy to write a trivial program that shows what the behavior is:
#!/usr/bin/perl
use strict;
use warnings;
if (#ARGV) {
output();
exit;
}
print "in the first program without \$|:\n";
output();
$| = 1;
print "in the first program with \$|:\n";
output();
print "in system with \$|\n";
system($^X, $0, 1) == 0
or die "could not run '$^X $0 1' failed\n";
$| = 0;
print "in system without \$|\n";
system($^X, $0, 1) == 0
or die "could not run '$^X $0 1' failed\n";
sub output {
for my $i (1 .. 4) {
print $i;
sleep 1;
}
print "\n";
}
From this we can see that setting $| has no affect on programs run through system.
This is something that you can easily check yourself. Create a program where buffering matters, like printing a series of dots. You should see the output all at once after ten seconds since the output is buffered:
#!perl
foreach ( 1 .. 10 )
{
print ".";
sleep 1;
}
print "\n";
Now, try setting $| and calling this with system:
% perl -e "$|++; system( qq|$^X test.pl| )";
For my test case, the $| value didn't affect the buffering in the child process.

How can I test STDIN without blocking in Perl?

I'm writing my first Perl app -- an AOL Instant Messenger bot that talks to an Arduino microcontroller, which in turn controls a servo that will push the power button on our sysadmin's server, which freezes randomly every 28 hours or so.
I've gotten all the hard stuff done, I'm just trying to add one last bit of code to break the main loop and log out of AIM when the user types 'quit'.
The problem is, if I try to read from STDIN in the main program loop, it blocks the process until input is entered, essentially rendering the bot inactive. I've tried testing for EOF before reading, but no dice... EOF just always returns false.
Here's below is some sample code I'm working with:
while(1) {
$oscar->do_one_loop();
# Poll to see if any arduino data is coming in over serial port
my $char = $port->lookfor();
# If we get data from arduino, then print it
if ($char) {
print "" . $char ;
}
# reading STDIN blocks until input is received... AAARG!
my $a = <STDIN>;
print $a;
if($a eq "exit" || $a eq "quit" || $a eq 'c' || $a eq 'q') {last;}
}
print "Signing off... ";
$oscar->signoff();
print "Done\n";
print "Closing serial port... ";
$port->close() || warn "close failed";
print "Done\n";
The Perl built-in is select(), which is a pass-through to the select() system call, but for sane people I recommend IO::Select.
Code sample:
#!/usr/bin/perl
use IO::Select;
$s = IO::Select->new();
$s->add(\*STDIN);
while (++$i) {
print "Hiya $i!\n";
sleep(5);
if ($s->can_read(.5)) {
chomp($foo = <STDIN>);
print "Got '$foo' from STDIN\n";
}
}
I found that IO::Select works fine as long as STDOUT gets closed, such as when the upstream process in the pipeline exits, or input is from a file. However, if output is ongoing (such as from "tail -f") then any partial data buffered by <STDIN> will not be displayed. Instead, use the unbuffered sysread:
#!/usr/bin/perl
use IO::Select;
$s = IO::Select->new(\*STDIN);
while (++$i) {
if ($s->can_read(2)) {
last unless defined($foo=get_unbuf_line());
print "Got '$foo'\n";
}
}
sub get_unbuf_line {
my $line="";
while (sysread(STDIN, my $nextbyte, 1)) {
return $line if $nextbyte eq "\n";
$line .= $nextbyte;
}
return(undef);
}