7

I have a Perl script running on an old CentOS 5.6 server which has Perl 5.8.8 installed. Unfortunately I can't upgrade either the OS or the version of Perl that's running on this server.

When I'm running this script from the command prompt, despite there being a $| = 1; statement at the top of the script (in the global scope), it still seems to buffer output to the console (over a ssh session).

Writes to both a log file and STDOUT are carried out by a function, for example:

#!/usr/bin/perl
$| = 1;

&writelog("Started...");

# Do work with lots of writelog'ing

&writelog("...Done.");

exit(0);

sub writelog {

    # This is greatly simplified for the purpose of this question

    my ($logentry) = @_;
    my $logfile = "/var/log/thelog.log";
    my $logline = "$logentry\n";
    print $logline;
    open (LOGFILE, ">>$logfile");
    print LOGFILE, "$logline";
    close (LOGFILE);
}

Does the value of $| only affect output in the current scope, i.e. in this case the script's global scope? Or should it, in the example above, also cause immediate flushing to STDOUT/LOGFILE by the print statements in writelog?

Kev
  • 118,037
  • 53
  • 300
  • 385
  • I don't see why `print $logline;` would be buffered with `$|` set, the good answers notwithstanding. Perhaps `ssh` is to blame? Btw, that `&` in front of function calls [doesn't seem needed](https://stackoverflow.com/a/8915304/4653379). – zdim Jul 23 '17 at 23:36

4 Answers4

8

$| affects only the currently selected default output filehandle.

You can explicitly set it for a filehandle like:

LOGFILE->autoflush(1);

See http://perldoc.perl.org/perlvar.html#Variables-related-to-filehandles

ysth
  • 96,171
  • 6
  • 121
  • 214
7

$| isn't a pragma; changing $| changes a flag in the currently selected file handle in a "permanent" fashion. By default, that handle is STDOUT.

You can affect other file handles by using

use IO::Handle qw( );  # Only required on older versions of Perl.
$handle->autoflush(1);

This is a shortcut for

my $temp = select($handle); $| = 1; select($temp);

except it tries to handle exceptions.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    Trivia: `print $foo;` isn't short for `print STDOUT $foo;`; it's short for `print { select() } $foo;`. – ikegami Jun 19 '17 at 15:58
  • Btw, what `{ local $| = 1; }` does to the handle, and how to check whether handle is already set to autoflush? – mpapec Jun 19 '17 at 16:05
  • 1
    @Сухой2, Turns autoflush on for the selected handle, then immediately restores the old setting. This will have the effect of flushing the handle. This is abstracted by `$handle->flush`. /// You can read `$|` to obtain the current setting for the currently selected handle. – ikegami Jun 19 '17 at 16:37
3

Per perlvar, $| has global scope and it applies only to the currently selected output channel. This is STDOUT at the beginning of any program, but can be changed with the 1-arg select call.

$| = 1;            # set autoflush on STDOUT
open LOGFILE, '>log';
my $fh = select LOGFILE;   # change "selected output channel"
$| = 1;            # set autoflush on LOGFILE
select $fh;        # restore STDOUT as "selected output channel"
$| = 0;            # turnoff autoflush on STDOUT
mob
  • 117,087
  • 18
  • 149
  • 283
0

If what you do is not working, you should look somewhere else.

If you redirect ssh to a file, it will not use a pseudo-terminal.

So you may want to use "ssh -t" to force the use of a pseudo-terminal.

You can also use the unbuffered write in perl :

syswrite(STDOUT, "Hello\n");
syswrite(LOGFILE, $logline);

Note that it is not a good idea to mix buffered and unbuffered writes on the same file handle.

Vouze
  • 1,678
  • 17
  • 10