Reading, writing, appending#

The three everyday tasks — read an existing file, create one from scratch, add to the end of one — are three different modes of the same open call. This page shows each in the form that actually belongs in new code.

The three-argument form#

open my $fh, MODE, EXPR  or die "open: $!";
  • my $fh is a fresh lexical. open fills it in with a handle reference when it succeeds; on failure it leaves $fh undefined and sets $!.

  • MODE is a short string — "<", ">", ">>", and a handful of others — that says what you want to do.

  • EXPR is the pathname. It is treated as a plain string: leading or trailing whitespace is preserved, and the string is never reinterpreted as a shell command.

The older two-argument form (open $fh, "<$path") still works, but it re-parses the string looking for mode characters and for the “magic” pipe syntax. Do not feed it a filename that came from a user.

Reading#

my $path = "/etc/hostname";
open my $fh, "<", $path   or die "open $path: $!";

while (my $line = <$fh>) {
    chomp $line;
    # ... work with $line ...
}

close $fh                 or die "close $path: $!";

Two observations that trip up beginners:

  • <$fh> calls readline under the hood. In boolean context — as inside the while head — the result is tested for being defined, not truthy. That matters because a file containing the line "0\n" would otherwise stop the loop early. while (<$fh>) is a special-case spelling that Perl rewrites to while (defined($_ = <$fh>)) for you.

  • close can fail. On a read handle that is rare, but on a write handle it is the moment buffered data is finally flushed, so a closing error is a real error. Check it.

If you want the whole file as one string, slurp it:

open my $fh, "<", $path or die "open $path: $!";
local $/;                               # undef = slurp mode
my $all = <$fh>;
close $fh               or die "close $path: $!";

The local $/ is the idiomatic “for the rest of this scope, turn off line splitting”. When control leaves the enclosing block, $/ returns to its previous value.

Writing (clobber)#

open my $fh, ">", $path   or die "open $path: $!";
print $fh "first line\n";
print $fh "second line\n";
close $fh                 or die "close $path: $!";
  • ">" creates the file if it does not exist, and truncates it to zero bytes if it does. There is no confirmation step.

  • The filehandle does not need a comma after it in a print invocation. print $fh "text" is correct; print $fh, "text" is a different expression that treats $fh as a list element.

  • Buffered writes are held in memory until close, an explicit flush, or the buffer fills. If your program crashes between print and close, a writer with pending buffered data loses it. Always pair a writing open with a checked close.

Appending#

open my $fh, ">>", $path  or die "open $path: $!";
print $fh scalar(localtime), " event\n";
close $fh                 or die "close $path: $!";
  • ">>" creates the file if needed and positions the write cursor at the end.

  • On a POSIX kernel, each write(2) on an append-mode descriptor is atomic with respect to the file’s end-of-file marker. Two processes appending short lines to the same log therefore interleave line-by-line, not byte-by-byte. Do not rely on that for lines longer than PIPE_BUF bytes.

Closing as a real step, not a formality#

A failed close on a write handle typically means the kernel refused to flush — out of space, I/O error, network filesystem trouble. Treat it exactly like a failed print:

close $fh or die "close $path: $!";

Ignoring it is how “the file looked fine during testing” becomes “production wrote half a log entry and moved on.”

What about read-write?#

"+<" opens a file for both reading and writing, without truncating. It is a real mode and it works, but it is also the source of most “my file came out corrupted” bug reports. If you need random-access reads with in-place overwrites, reach for sysopen, sysread, and syswrite so the buffering layer does not guess at seek positions on your behalf.

For almost every job labelled “edit the file”, the right answer is write a new file, fsync, rename over the old one. That is the pattern atomic editors use.

Next#

With reading, writing, and appending covered, the next question is what the bytes mean. See Encoding and layers.