Skip to content
VLSI Mentor

Perl Part I · Module 13 · File Handling

open and close: The Three-Argument Form

Almost every Perl script in this field reads a file. This chapter is how. You will learn one function, open, and one rule about how to call it. The rule matters: there is an old way to call open that is still legal, still common in scripts you will inherit, and able to destroy a file you only meant to read. Section four measures exactly that — the same filename, handled two ways, with one of them leaving the file empty. We start with the smallest working example, then the three modes, then what open hands back so you can check it. Each section builds on the one before, and every output on this page comes from running the code shown.

Foundation11 min readPerlPart IFile Handling

Part I · Module 13 · Chapter 13.1 · open and close: The Three-Argument Form

1. The Smallest Example

Write two lines to a file, then read them back.

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

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

# WRITE: the > mode creates the file (or empties an existing one)
open my $out, ">", $file or die "cannot write $file: $!";
print $out "u_alu/A -0.12\n";
print $out "u_fifo/B 0.44\n";
close $out;

# READ: the < mode opens it for reading
open my $in, "<", $file or die "cannot read $file: $!";
while (my $line = <$in>) {
    chomp $line;
    print "got: $line\n";
}
close $in;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
got: u_alu/A -0.12
got: u_fifo/B 0.44

That is the whole pattern. Look at the shape of each open call, because it is the same every time:

open my $fh, MODE, FILENAME or die "...: $!";

Four parts, in order:

  1. my $fh — a new variable to hold the filehandle. Perl fills it in for you.
  2. MODE — a short string saying what you want to do. < to read, > to write.
  3. FILENAME — the path, on its own, as a separate argument.
  4. or die — what to do if it fails. Chapter 13.5 is entirely about this part.

Then print $fh ... writes to it, <$fh> reads from it, and close releases it. Note there is no comma after $out in print $out "..."; — that is the filehandle slot, not an argument.

The script uses File::Temp to get a scratch directory so the example is safe to run and cleans up after itself. Your own scripts will use a real path.

2. The Three Modes

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

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

# Read the file back and show it on one line.
sub contents {
    open my $fh, "<", $file or return "(file missing)";
    my @lines = <$fh>;
    close $fh;
    chomp @lines;
    return join " / ", @lines;
}

open my $a, ">", $file  or die $!;  print $a "first\n";   close $a;
printf "after  >  : %s\n", contents();

open my $b, ">", $file  or die $!;  print $b "second\n";  close $b;
printf "after  >  : %s        <- the second > EMPTIED the file\n", contents();

open my $c, ">>", $file or die $!;  print $c "third\n";   close $c;
printf "after  >> : %s  <- >> added to the end\n", contents();
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
after  >  : first
after  >  : second        <- the second > EMPTIED the file
after  >> : second / third  <- >> added to the end

Three modes cover nearly everything you will do:

  • < — read. The file must already exist.
  • > — write. Creates the file if it is missing, and empties it if it is not.
  • >> — append. Creates the file if missing, and adds to the end if it exists.

The middle line of output is the one to remember. Opening with > threw away first before writing second. That is not an error and there is no warning — it is what > means. If you want to add to a file, you want >>.

Chapter 13.4 covers writing in more detail, and Chapter 13.12 adds the modes for binary and encoded files.

3. What open Gives Back

open does not stop your program when it fails. It returns a value, and it is your job to look at it.

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

my $dir = tempdir(CLEANUP => 1);

# open does NOT die on its own. It returns false and sets $!.
my $missing = "$dir/not-here.txt";
my $result  = open my $fh, "<", $missing;

printf "open returned  : %s\n", (defined $result ? "[$result]" : "undef");
printf "is it true     : %s\n", ($result ? "yes" : "no");
printf "\$! explains why : %s\n", $!;

# On success it returns a true value.
my $good = "$dir/new.txt";
my $ok   = open my $out, ">", $good;
printf "\nopen for write : %s\n", ($ok ? "true" : "false");
print $out "hello\n";
printf "close returned : %s\n", (close($out) ? "true" : "false");
printf "file now exists: %s\n", (-e $good ? "yes" : "no");
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
open returned  : undef
is it true     : no
$! explains why : No such file or directory

open for write : true
close returned : true
file now exists: yes

On failure open returned undef, and the reason went into the special variable $! — here No such file or directory.

This is why every open in this chapter ends with or die. Without it, the program carries on with a filehandle that was never opened, and the next print or read fails somewhere else with a less helpful message. Chapter 13.5 measures what that looks like.

close also returns true or false. Checking it matters more than people think when writing, because that is when data is actually flushed to disk — a failing close can mean a full filesystem. Chapter 13.4 returns to this.

4. Why Three Arguments: a Measured Data Loss

Now the advanced part, and the reason this chapter's title says "three-argument form".

There is an older way to call open with only two arguments, where the mode and the filename are joined in one string. It is still legal Perl. Here is what it allows.

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

my $dir = tempdir(CLEANUP => 1);

# Set up a file we care about.
open my $seed, ">", "$dir/important.txt" or die $!;
print $seed "DO NOT LOSE THIS\n";
close $seed;

# A filename that arrived from somewhere else -- a config file, @ARGV, a user.
my $name = ">$dir/important.txt";       # note the leading >

# THE OLD TWO-ARGUMENT FORM: mode and name in ONE string.
if (open my $fh, $name) {               # no mode argument at all
    close $fh;
    printf "two-arg open  : succeeded\n";
}
open my $check, "<", "$dir/important.txt" or die $!;
my $after = <$check> // "(empty)";
close $check;
chomp $after if defined $after && $after ne "(empty)";
printf "file now holds: %s\n", ($after eq "(empty)" ? "(EMPTY -- it was truncated)" : $after);

# THE THREE-ARGUMENT FORM treats the whole string as a filename.
open my $seed2, ">", "$dir/safe.txt" or die $!;
print $seed2 "STILL HERE\n"; close $seed2;

my $ok = open my $fh3, "<", ">$dir/safe.txt";
printf "\nthree-arg open with the same string : %s\n", ($ok ? "opened" : "refused");
open my $check2, "<", "$dir/safe.txt" or die $!;
my $after2 = <$check2>; close $check2; chomp $after2;
printf "file still holds                    : %s\n", $after2;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
two-arg open  : succeeded
file now holds: (EMPTY -- it was truncated)

three-arg open with the same string : refused
file still holds                    : STILL HERE

Read those four lines carefully.

The script wanted to read a file. The filename it was given happened to start with >. The two-argument form looked at that string, saw a >, decided it meant "write", and emptied the file. The program did not crash. It reported success.

The three-argument form was given the same string. Because the mode is a separate argument, the > was just the first character of a filename — a file by that odd name does not exist, so the open was refused and the real file was untouched.

Two related habits from the same family:

Use a lexical filehandle. open my $fh, ... makes a filehandle held in an ordinary my variable. The old style used a bareword like open FH, ..., which is a global — so two parts of a program using FH interfere with each other, exactly as Chapter 12.7 described for package variables. A my filehandle also closes itself when the variable goes out of scope.

Put the path in a variable first. open my $fh, "<", $file or die "cannot read $file: $!"; lets the error message name the file. An error that says which path failed is worth far more than one that does not.

5. What To Remember

  • The shape is always the same: open my $fh, MODE, FILENAME or die "...: $!";
  • Three modes: < read, > write, >> append.
  • > empties an existing file. Measured: the second > threw away first. No error, no warning.
  • open returns false on failure and sets $! — measured as undef and No such file or directory. It does not stop your program for you.
  • close returns a value too, and it matters most when writing, because that is when data reaches the disk.
  • Always pass the mode as a separate argument. Measured: the two-argument form emptied a file it was asked to read, while the three-argument form refused the same string and the file survived.
  • A filename is data, not an instruction. That is why the rule exists.
  • Use open my $fh, not a bareword handle — barewords are globals.
  • Name the file in the error message.
  • No comma after the handle: print $fh "text";

Chapter 13.2 takes the read loop from section one and looks at it properly: what <$fh> returns, why while is the right loop, why chomp is almost always needed, and the one condition that makes the loop safe on a line containing just 0.

Where this fits

Part of the Perl curriculum.