PERLOPENTUT(1)

πŸ’– PERLOPENTUT(1) πŸ’–

okay bestie, welcome to perlopentut, part of the Perl Programmers Reference Guide ✨ this is the page that teaches you how to open files and pipes in Perl, and honestly? it's giving essential.


🌸 NAME 🌸

perlopentut β€” simple recipes for opening files and pipes in Perl πŸŽ€


πŸ¦‹ DESCRIPTION πŸ¦‹

So. Every single time you do I/O on a file in Perl, you're doing it through something called a filehandle. A filehandle is just an internal nickname for some external file. The open function's whole job is to link the internal nickname to the external file, and the close function's whole job is to break that link. iconic teamwork honestly πŸ’…

And because Perl loves you, it sets up a few special filehandles that are already open the second you run. Those are STDIN, STDOUT, STDERR, and ARGV. They come pre-opened, so you can just... use them. no setup, no drama:

    print STDERR "This is a debugging message.\n";

    print STDOUT "Please enter something: ";
    $response = <STDIN> // die "how come no input?";
    print STDOUT "Thank you!\n";

    while (<ARGV>) { ... }

See how that works? STDOUT and STDERR are output handles, STDIN and ARGV are input handles 🫢 they're in ALL CAPS because they're reserved for Perl, same energy as the @ARGV array and the %ENV hash. their external associations were set up by your shell, not by you.

Every other filehandle? You open that yourself. no one's doing it for you, bestie 😭 there are like a million variants, but the most common way to call Perl's open() is three arguments and one return value:

    OK = open(HANDLE, MODE, PATHNAME)

Where:

  • OK β€” will be some defined value if the open succeeds, but undef if it fails 🫠
  • HANDLE β€” should be an undefined scalar variable, and the open function fills it in if it succeeds
  • MODE β€” the access mode and the encoding format you're opening the file with
  • PATHNAME β€” the external name of the file you want opened

Most of the complexity of open lives in the many possible values MODE can take on. that's the main character of this whole page, fr fr πŸ‘‘

One last thing before we get into actually opening files: opening files does not (usually) automatically lock them in Perl. See perlfaq5 for how to lock. don't forget this one πŸ”’


✨ Opening Text Files ✨

πŸ“ Opening Text Files for Reading πŸ“

Wanna read from a text file? First open it in read-only mode, like this:

    my $filename = "/some/path/to/a/textfile/goes/here";
    my $encoding = ":encoding(UTF-8)";
    my $handle   = undef;     # this will be filled in on success

    open($handle, "< $encoding", $filename)
        || die "$0: can't open $filename for reading: $!";

Just like in the shell, < means open the file in read-only mode. If it works, Perl allocates a brand new filehandle for you and fills in your previously undefined $handle argument with a reference to that handle. slay πŸ’…

Now you can use functions like readline, read, getc, and sysread on that handle. The most common input function is probably the one that looks like an operator:

    $line = readline($handle);
    $line = <$handle>;          # same thing

Because readline returns undef at end of file or on error, you'll sometimes see it used like this:

    $line = <$handle>;
    if (defined $line) {
        # do something with $line
    }
    else {
        # $line is not valid, so skip it
    }

You can also just quickly die on an undefined value this way:

    $line = <$handle> // die "no input found";

But listen β€” if hitting EOF is an expected, normal, everyday event, you do not want to exit just because you ran out of input. What you probably want is to exit an input loop. Then you can test whether an actual error is what terminated the loop, and act accordingly:

    while (<$handle>) {
        # do something with data in $_
    }
    if ($!) {
        die "unexpected error while reading from $filename: $!";
    }

A Note on Encodings: having to specify the text encoding every single time is kinda a lot, ngl. To set up a default encoding for open so you don't have to supply it each time, you can use the open pragma:

    use open qw< :encoding(UTF-8) >;

Once you've done that, you can safely omit the encoding part of the open mode:

    open($handle, "<", $filename)
        || die "$0: can't open $filename for reading: $!";
🚨 but never use the bare < without having set up a default encoding first. Otherwise Perl cannot know which of the many, many, many possible flavors of text file you have, and Perl will have no idea how to correctly map the data in your file into actual characters it can work with. 😭

Other common encoding formats include ASCII, ISO-8859-1, ISO-8859-15, Windows-1252, MacRoman, and even UTF-16LE. See perlunitut for more about encodings 🌈

πŸŽ€ Opening Text Files for Writing πŸŽ€

When you want to write to a file, you first have to decide what to do about any existing contents of that file. You've got two basic choices here: preserve or clobber. pick your fighter.

If you want to preserve any existing contents, you want to open the file in append mode. Same as the shell β€” in Perl you use >> to open an existing file in append mode. And >> creates the file if it doesn't already exist 🫢

    my $handle   = undef;
    my $filename = "/some/path/to/a/textfile/goes/here";
    my $encoding = ":encoding(UTF-8)";

    open($handle, ">> $encoding", $filename)
        || die "$0: can't open $filename for appending: $!";

Now you can write to that filehandle using any of print, printf, say, write, or syswrite ✍️

As noted above, if the file doesn't already exist, the append-mode open creates it for you. But if the file does already exist, its contents are safe from harm, because you're adding your new text past the end of the old text. no cap, your old data is protected πŸ›‘οΈ

On the other hand, sometimes you wanna clobber whatever might already be there. To empty out a file before you start writing to it, open it in write-only mode:

    my $handle   = undef;
    my $filename = "/some/path/to/a/textfile/goes/here";
    my $encoding = ":encoding(UTF-8)";

    open($handle, "> $encoding", $filename)
        || die "$0: can't open $filename in write-open mode: $!";

And here again Perl works just like the shell, in that > clobbers an existing file πŸ’€

Same as with append mode, once you open a file in write-only mode you can write to that filehandle using any of print, printf, say, write, or syswrite.

What about read-write mode? bestie... you should probably pretend it doesn't exist πŸ™ˆ because opening text files in read-write mode is unlikely to do what you would like. See perlfaq5 for details.

🧚 Opening Binary Files 🧚

If the file you're opening contains binary data instead of text characters, then the MODE argument to open is a little different. Instead of specifying the encoding, you tell Perl that your data are in raw bytes.

    my $filename = "/some/path/to/a/binary/file/goes/here";
    my $encoding = ":raw :bytes"
    my $handle   = undef;     # this will be filled in on success

And then open as before, choosing <, >>, or > as needed:

    open($handle, "< $encoding", $filename)
        || die "$0: can't open $filename for reading: $!";

    open($handle, ">> $encoding", $filename)
        || die "$0: can't open $filename for appending: $!";

    open($handle, "> $encoding", $filename)
        || die "$0: can't open $filename in write-open mode: $!";

Alternately, you can switch to binary mode on an existing handle this way:

    binmode($handle)    || die "cannot binmode handle";

This is especially handy for the handles Perl already opened for you ✨

    binmode(STDIN)      || die "cannot binmode STDIN";
    binmode(STDOUT)     || die "cannot binmode STDOUT";

You can also pass binmode an explicit encoding to change it on the fly. This isn't exactly "binary" mode, but we still use binmode to do it:

  binmode(STDIN,  ":encoding(MacRoman)") || die "cannot binmode STDIN";
  binmode(STDOUT, ":encoding(UTF-8)")    || die "cannot binmode STDOUT";

Once your binary file is properly opened in the right mode, you can use all the same Perl I/O functions you used on text files. However, you may wish to use the fixed-size read instead of the variable-sized readline for your input πŸ‘‘

Here's an example of how to copy a binary file:

    my $BUFSIZ   = 64 * (2 ** 10);
    my $name_in  = "/some/input/file";
    my $name_out = "/some/output/file";

    my($in_fh, $out_fh, $buffer);

    open($in_fh,  "<", $name_in)
        || die "$0: cannot open $name_in for reading: $!";
    open($out_fh, ">", $name_out)
        || die "$0: cannot open $name_out for writing: $!";

    for my $fh ($in_fh, $out_fh)  {
        binmode($fh)               || die "binmode failed";
    }

    while (read($in_fh, $buffer, $BUFSIZ)) {
        unless (print $out_fh $buffer) {
            die "couldn't write to $name_out: $!";
        }
    }

    close($in_fh)       || die "couldn't close $name_in: $!";
    close($out_fh)      || die "couldn't close $name_out: $!";

and that's the whole binary copy recipe, chef's kiss πŸ’‹

πŸŽ€ Opening Pipes πŸŽ€

Okay bestie, plot twist: Perl doesn't only open filehandles into files. It can also open one straight into an external program or a shell command. Like, you're not reading a static file anymore, you're reading the vibes of a whole other process. πŸ’…

Why would you even do that? Two reasons, and both are iconic:

  • You wanna pass data from your Perl program to an external command so it can do further processing.
  • You wanna receive data from another program so your Perl program can process it.

Filehandles into commands are also called pipes, because they run on the same inter-process communication principles as Unix pipelines. No cap. The external end of such a filehandle has an active program instead of a static file, but literally everything else works just like a normal file-based filehandle β€” every technique we talked about earlier still applies. 🫢

So you open a pipe with the exact same open call you use for files, you just set that second (MODE) argument to special characters that say "this is an input pipe" or "this is an output pipe":

  • Use -| for a filehandle that lets your Perl program read data from an external program.
  • Use |- for a filehandle that sends data to that program instead.

🌸 Opening a pipe for reading 🌸

Picture this: you want your Perl program to process data sitting in a nearby directory called unsorted, which has a bunch of textfiles in it. And you want all that content merged into a single, alphabetically sorted list of unique lines before processing even starts. Main character setup, honestly.

You could open an ordinary filehandle into each file, slowly build up an in-memory array of all the contents, and then sort and filter it once you run out of files. OR β€” and hear me out β€” you could offload all that merging and sorting to your operating system's own sort command by opening a pipe directly into its output. Way faster. Obsessed. ✨

Here's how that looks:

open(my $sort_fh, '-|', 'sort -u unsorted/*.txt')
    or die "Couldn't open a pipe into sort: $!";

# And right away, we can start reading sorted lines:
while (my $line = <$sort_fh>) {
    #
    # ... Do something interesting with each $line here ...
    #
}

That second argument to open, -|, is what makes it a read-pipe into a separate program instead of an ordinary filehandle into a file. It's giving "I'm not like other filehandles." πŸ’€

Also note that the third argument is a string holding the program name (sort) plus all its arguments: here -u to say "unique sort", and then a fileglob specifying the files to sort. The resulting filehandle $sort_fh works exactly like a read-only (<) filehandle, and your program can read from it as if it were opened onto one ordinary, single file. Slay.


πŸ’– Opening a pipe for writing πŸ’–

Continuing the previous example: say your program finished processing, and the results are chilling in an array called @processed. Now you wanna print those lines to a file called numbered.txt with a neatly formatted column of line-numbers. Cute, right?

Sure, you could write your own code for that. Or β€” once again β€” you could hand the job to another program. In this case cat, running with its own -n option to activate line numbering, should do the trick:

open(my $cat_fh, '|-', 'cat -n > numbered.txt')
    or die "Couldn't open a pipe into cat: $!";

for my $line (@processed) {
    print $cat_fh $line;
}

Here we use a second open argument of |-, which means the filehandle assigned to $cat_fh should be a write-pipe. Then you just use it like a write-only ordinary filehandle, including the basic function of print-ing data to it. Periodt.

Okay so this next bit trips people up, so pay attention bestie πŸ‘€ β€” the third argument specifies the command we wanna pipe to, and it sets up cat to redirect its output via that > symbol into the file numbered.txt. Which looks kinda tricky, because that same symbol would've meant something entirely different if it had shown up in the second argument to open! But here in the third argument it's simply part of the shell command Perl opens the pipe into, and Perl itself invests no special meaning in it.

πŸ¦‹ Expressing the command as a list πŸ¦‹

For opening pipes, Perl offers the option to call open with a list: the desired command plus all its own arguments as separate elements, rather than mashing them into one string like in the examples above.

So for instance, we could've phrased the open call from the first example like this:

open(my $sort_fh, '-|', 'sort', '-u', glob('unsorted/*.txt'))
    or die "Couldn't open a pipe into sort: $!";

When you call open this way, Perl invokes the given command directly, bypassing the shell entirely. Which means the shell won't try to interpret any special characters inside the command's argument list β€” and those could've had unwanted effects otherwise. This makes for safer, less error-prone open calls, super useful for things like passing in variables as arguments, or honestly just referring to filenames that have spaces in them. πŸ™ˆ

But β€” and this is the catch β€” when you do want to pass a meaningful metacharacter to the shell, like that * inside the final unsorted/*.txt argument, you can't use this alternate syntax. In this case we worked around it with Perl's handy glob built-in function, which evaluates its argument into a list of filenames β€” and we can safely pass that resulting list right into open, as shown above. Problem solved, we ate. πŸ“

Note also that representing piped-command arguments in list form like this doesn't work on every platform. It works on any Unix-based OS that provides a real fork function (e.g. macOS or Linux), and also on Windows when running Perl 5.22 or later.

⭐ SEE ALSO ⭐

The full documentation for open gives you a thorough reference to this function, way beyond the best-practice basics we covered here. Go read it, iykyk. πŸ“š


Copyright 2013 Tom Christiansen; now maintained by Perl5 Porters

This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself.


And that's a wrap, bestie!! This whole masterpiece is perl v5.42.3, dated 2026-08-03, and it lives in the PERLOPENTUT(1) manpage. Go open some pipes, you absolute icon πŸ’–βœ¨πŸŽ€πŸ¦‹πŸŒΈπŸ’…πŸ«Ά

πŸ“¦ where this page comes from