How do I flush a file in Perl?

I have Perl script which appends a new line to the existing file every 3 seconds. Also, there is a C++ application which reads from that file.

The problem is that the application begins to read the file after the script is done and file handle is closed. To avoid this I want to flush after each line append, but I'm new to Perl and don't know how to do that.

Answers


Try:

use IO::Handle;
$fh->autoflush;

This was actually posted as a way of auto-flushing in an early question of mine, which asked about the universally accepted bad way of achieving this :-)


TL/DR: use IO::Handle and the flush method, eg:

use IO::Handle;
$myfile->flush();

First, you need to decide how "flushed" you want it. There can be quite a few layers of buffering:

  • Perl's internal buffer on the file handle. Other programs can't see data until it's left this buffer.

  • File-system level buffering of "dirty" file blocks. Other programs can still see these changes, they seem "written", but they'll be lost if the OS or machine crashes.

  • Disk-level write-back buffering of writes. The OS thinks these are written to disk, but the disk is actually just storing them in volatile memory on the drive. If the OS crashes the data won't be lost, but if power fails it might be unless the disk can write it out first. This is a big problem with cheap consumer SSDs.

It gets even more complicated when SANs, remote file systems, RAID controllers, etc get involved. If you're writing via pipes there's also the pipe buffer to consider.

If you just want to flush the Perl buffer, you can close the file, print a string containing "\n" (since it appears that Perl flushes on newlines), or use IO::Handle's flush method.

You can also, per the perl faq use binmode or play with $| to make the file handle unbuffered. This is not the same thing as flushing a buffered handle, since queuing up a bunch of buffered writes then doing a single flush has a much lower performance cost than writing to an unbuffered handle.

If you want to flush the file system write back buffer you need to use a system call like fsync(), open your file in O_DATASYNC mode, or use one of the numerous other options. It's painfully complicated, as evidenced by the fact that PostgreSQL has its own tool just to test file syncing methods.

If you want to make sure it's really, truly, honestly on the hard drive in permanent storage you must flush it to the file system in your program. You also need to configure the hard drive/SSD/RAID controller/SAN/whatever to really flush when the OS asks it to. This can be surprisingly complicated to do and is quite OS/hardware specific. "plug-pull" testing is strongly recommended to make sure you've really got it right.


From 'man perlfaq5':

$old_fh = select(OUTPUT_HANDLE);
$| = 1;
select($old_fh);

If you just want to flush stdout, you can probably just do:

$| = 1;

But check the FAQ for details on a module that gives you a nicer-to-use abstraction, like IO::Handle.


All of the solutions suggesting setting autoflush are ignoring the basic fact that most modern OS's are buffering file I/O irrespective of what Perl is doing.

You only possibility to force the commitment of the data to disk is by closing the file.

I'm trapped with the same dilemma atm where we have an issue with rotation of the log being written.


Here's the answer, the real answer.

STOP maintaining an open file handle for this file for the life of the process.

START abstracting your file-append operation into a sub that opens the file in append mode, writes to it, closes it.

#appends a new line to the existing file
sub append_new_line{
    my $linedata = shift;
    open my $fh, '>>', $fnm or die $!; # $fnm is file-lexical or something
    print $fh $linedata,"\n"; # flavor to taste
    close $fh;
}

the process observing the file will encounter a closed file that gets modified whenever the function is called.


To automatically flush the output, you can set autoflush/$| as described by others before you output to the filehandle.

If you've already output to the filehandle and need to ensure that it gets to the physical file, you need to use the IO::Handle flush and sync methods.


There an article about this in PerlDoc: How do I flush/unbuffer an output filehandle? Why must I do this?

Two solutions:

  • Unbuffer the output filehandler with : $|
  • Call the autoflush method if you are using IO::Handle or one of its subclasses.

An alternative approach would be to use a named pipe between your Perl script and C++ program, in lieu of the file you're currently using.


I had the same problem with the only difference of writing the same file over and over again with new content. This association of "$| = 1" and autoflush worked for me:

 open  (MYFILE, '>', '/internet/web-sites/trot/templates/xml_queries/test.xml');
 $| = 1; # Before writing!
 print  MYFILE "$thisCardReadingContentTemplate\n\n";
 close (MYFILE);
 MYFILE->autoflush(1); # After writing!

Best of luck. H


Need Your Help

How to review a pull request in intelliJ IDEA?

git intellij-idea bitbucket

We are using git, bitbucket, and IntelliJ IDEA. The project is mainly written in Java and Groovy.

Creating an admin user in Devise on Rails beta 3

ruby-on-rails devise

Ok, I'm probably going to feel quite dumb when someone answers this one with a simple thing that I'm missing but... here goes: