views:

95

answers:

4

I'm starting to learn perl, using the Wrox Beginning Perl available on perl.org and have a question regarding a for loop example they provide in Chapter 3.

#!/usr/bin/perl

use warnings;
use strict;

my @count = (1..10);
for (reverse(@count)) {
        print "$_...\n";
        sleep 1;
}
print "Blast Off!\n"

This is the script they provide, and it works as expected. It displays a number followed by ... every second, waiting a second in between each number. When done, it displays Blast Off!

However if I remove the newline from the print statement, the behaviour changes. The script silently waits 10 seconds and then displays all 10 numbers and Blash Off! at once. Why the change?

#!/usr/bin/perl

use warnings;
use strict;

my @count = (1..10);
for (reverse(@count)) {
        print "$_...";
        sleep 1;
}
print "Blast Off!\n"
+4  A: 

Standard output is line buffered. It won't flush the buffer until it sees a newline. You can correct this by adding the following line before the loop:

my $ofh = select(STDOUT); $| = 1; select $ofh;

Let's break that down:

my $ofh = select(STDOUT); - selects the STDOUT (standard output) as the current selected file handle, and stores whatever was the previous selected file handle in $ofh

$| = 1; - sets the current file handle (ie. STDOUT) to unbuffered.

select $ofh; - reselects whatever was the selected file handle before, so that if the code had selected a file handle other than STDOUT at some point, it will still be selected after this line of code is finished.

Paul Tomblin
Care to explain the downvote?
Paul Tomblin
You should probably explain what the `select`s are for.
Brad Gilbert
@Brad, done. Thanks for the suggestion.
Paul Tomblin
+11  A: 

Perl is buffering the print output and the buffer isn't flushed until the newline. Alternatively, you can add $| = 1 before your loop to tell Perl to flush the buffer between the print statements, thus giving you the expected output with the proper delay:

#!/usr/bin/perl

use warnings;
use strict;

$| = 1; #Add this line
my @count = (1..10);
for (reverse(@count)) {
        print "$_...";
        sleep 1;
}
print "Blast Off!\n"'
eldarerathis
The way I did it, with the select statements, is safer if there is any other file interaction.
Paul Tomblin
Even better, and more modern: use IO::Handle; STDOUT->autoflush(1);
mscha
+4  A: 

You are looking at the standard behavior of the line-oriented output of the C stdio library when writing to a terminal.

See autoflush for perl details.

msw
+1  A: 

A neater way than using select() to set the autoflush flag on STDOUT is to use IO::Handle (which has been included with the Perl core since version 5.004):

use IO::Handle;
STDOUT->autoflush(1)
ilmari