Skip to content
VLSI Mentor

Perl Part I · Module 13 · File Handling

Writing and Appending

Writing a file is the same open call you already know with a different mode, plus print. The two things worth your attention are both about losing data rather than about syntax. The first is the difference between starting a new file and adding to one, which matters most when a script is run a second time — section two runs the same script three times each way and counts what survives. The second is that print does not put your data on the disk. It puts it in a buffer, and close is what empties that buffer into the file. Section three measures a file sitting at zero bytes after the print has already happened. Once you have seen that, checking what close returns stops feeling like paranoia.

Foundation10 min readPerlPart IFile Handling

Part I · Module 13 · Chapter 13.4 · Writing and Appending

1. Three Ways To Write a Line

Azvya Education Pvt. Ltd.VLSI Mentor
write-basics.pl
use strict;
use warnings;
use File::Temp qw(tempdir);

my $dir = tempdir(CLEANUP => 1);
my $file = "$dir/out.txt";

open my $out, ">", $file or die "cannot write $file: $!";

print  $out "a plain line\n";                       # print: exactly what you give it
printf $out "%-10s %6.2f %s\n", "u_alu/A", -0.12, "ns";   # printf: formatted
print  $out join("\t", "u_fifo/B", "0.44", "ns"), "\n";   # a tab-separated row

close $out or die "cannot close $file: $!";

open my $in, "<", $file or die $!;
print while <$in>;
close $in;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
a plain line
u_alu/A     -0.12 ns
u_fifo/B	0.44	ns

Three rows written three ways, then the file read back and echoed.

print $fh "text" writes exactly the characters you give it. It adds nothing — no newline, no separator. If you want a line ending, you type \n.

printf $fh FORMAT, values formats first. %-10s is a left-aligned 10-character string and %6.2f is a number with two decimal places in a 6-wide field, which is how you get columns that line up. Module 4 covered the format codes.

join("\t", @fields) builds a tab-separated row, which is the easy format for another tool to read. Chapter 13.14 covers delimited output properly.

Two syntax points that trip people up:

  • No comma after the filehandle. print $out "text"; is correct; print $out, "text"; is wrong and prints the handle.
  • print while <$in>; on the second-to-last line is the compact echo idiom. The bare print prints $_, and the while puts each line in $_ — Chapter 11.2's bare-pattern default applied to printing.

Note also close $out or die .... That is section three.

2. > or >>: What Happens on the Second Run

Chapter 13.1 measured that > empties a file. Here is the shape that mistake actually takes.

Azvya Education Pvt. Ltd.VLSI Mentor
append-vs-truncate.pl
use strict;
use warnings;
use File::Temp qw(tempdir);

my $dir = tempdir(CLEANUP => 1);
my $file = "$dir/run.log";

sub lines {
    open my $fh, "<", $file or return 0;
    my @l = <$fh>; close $fh; return scalar @l;
}

# Three "runs" of a script that opens with >
for my $run (1 .. 3) {
    open my $fh, ">", $file or die $!;
    print $fh "run $run line 1\nrun $run line 2\n";
    close $fh;
}
printf "after 3 runs with  >  : %d line(s)\n", lines();

unlink $file;

# Three runs of a script that opens with >>
for my $run (1 .. 3) {
    open my $fh, ">>", $file or die $!;
    print $fh "run $run line 1\nrun $run line 2\n";
    close $fh;
}
printf "after 3 runs with >>  : %d line(s)\n", lines();
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
after 3 runs with  >  : 2 line(s)
after 3 runs with >>  : 6 line(s)

Six lines were written each way. With > the file holds 2. With >> it holds 6.

Neither number is wrong — they are two different intentions:

  • > for a result that replaces the previous one. A report, a generated netlist, a summary. You want last night's version gone.
  • >> for a record that accumulates. A log, an audit trail, a list of violations found across many runs.

The mistake is using > for the second kind, and it is hard to notice because the script works perfectly the first time. It only loses data on the re-run — which is the run after the one you tested.

3. close Is When the Writing Happens

Now the part that surprises people.

Azvya Education Pvt. Ltd.VLSI Mentor
close-matters.pl
use strict;
use warnings;
use File::Temp qw(tempdir);

my $dir = tempdir(CLEANUP => 1);
my $file = "$dir/buffered.txt";

open my $out, ">", $file or die $!;
print $out "this line is written\n";

# Before close, the data may still be in a buffer rather than on disk.
my $before = -s $file;
close $out;
my $after = -s $file;

printf "size before close : %s byte(s)\n", (defined $before ? $before : "0");
printf "size after  close : %d byte(s)\n", $after;
print  "\nclose is when the buffer reaches the file -- so check it when writing\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
size before close : 0 byte(s)
size after  close : 21 byte(s)

close is when the buffer reaches the file -- so check it when writing

The print had already run. The file was 0 bytes. After close it was 21.

Perl does not write each print straight to the disk — that would be extremely slow. It collects output in a buffer and writes it in blocks. The buffer is emptied when it fills up, or when you close the handle, or when the program ends.

Three consequences follow, and they are the practical content of this section.

Check what close returns when you are writing. A print can succeed and the close still fail, because the actual write to disk happens at the end. The usual cause is a full filesystem — which in this industry means a full scratch area, which happens constantly. close $fh or die "cannot close $file: $!"; is the line that turns a silently truncated report into an error you can act on.

Do not read a file you are still writing. If a script writes a file and another part of the same script opens it to read, the reader may see nothing. Close first.

For a progress log, turn buffering off. If you are writing a log you intend to tail while the job runs, the buffer means nothing appears for a long time and then a lot appears at once. Setting $fh->autoflush(1) — or $| for the selected handle, which Chapter 12.8 said to localise — makes each write go straight out. It is slower, and for a progress log that is the right trade.

One more habit worth adopting: write to a variable, then print once. Building a report in a string or an array and writing it in one print is both faster and easier to test than many small prints, because you can inspect the content before it goes anywhere.

4. What To Remember

  • print $fh "text" writes exactly what you give it, including no newline unless you add one. No comma after the handle.
  • printf $fh FORMAT, ... formats — %-10s and %6.2f are how columns line up.
  • join("\t", @fields) builds a tab-separated row.
  • > replaces, >> accumulates. Measured: three runs writing six lines left 2 lines with > and 6 with >>.
  • The > mistake only shows on the re-run, which is the run after the one you tested.
  • Two jobs writing one path with > interleave, with no error. Use a unique name or Chapter 13.13's atomic write.
  • print fills a buffer; close empties it to disk. Measured: 0 bytes after the print, 21 after the close.
  • Check close when writing — a full filesystem fails there, not at the print.
  • Do not read a file you have not closed.
  • Turn off buffering for a log you want to tail, and accept that it is slower.

Chapter 13.5 takes the or die "$!" that has been attached to every open on these pages and explains it properly — what $! holds, why an unchecked open fails somewhere else entirely, and what the error message should say to be useful.

Where this fits

Part of the Perl curriculum.