Perl beginner

How to Write to a File in Perl (open/print, modes, safety, and best practices)

Question

how to write to file in perl

How to write to a file in Perl

Category: Perl

Difficulty: Beginner (with intermediate best practices)

Tags: perl, file-io, open, print, say, append, encoding, autodie, flock, atomic-write, syswrite

Overview

Writing to a file in Perl is built around three ideas: (1) open a filehandle with the correct mode, (2) write using print / say / printf (or lower-level syswrite), and (3) close the filehandle and handle errors. Perl can write to files efficiently and safely, but many bugs come from skipping error checks, using the old two-argument form of open, mixing encodings, or accidentally truncating a file.

The essential pattern

The modern, recommended pattern is the three-argument open with a lexical filehandle:

open my $fh, '>', $path or die "open($path): $!";
print {$fh} "Hello\n";
close $fh or die "close($path): $!";

The three arguments matter: the mode ('>', '>>', etc.) is separate from the filename. This avoids a class of security problems and parsing surprises that come with older forms.

File modes you will use most

  • Overwrite / create: '>' (truncates an existing file to zero length)
  • Append / create: '>>' (writes go to end of file)
  • Read/write: '+<' (update without truncation; you manage seeking)
  • Read/write with truncation: '+>' (truncates like '>')

If you only remember one pitfall: '>' truncates immediately when you open the file. If you meant to add a line to an existing log, you want '>>', not '>'.

Example 1: Create/overwrite a file and read it back (runnable)

This example writes a small file using print, closes it, then reopens it for reading and prints the contents to STDOUT so you can see exactly what was written.

#!/usr/bin/env perl
use strict;
use warnings;

my $path = 'example1.txt';

open my $out, '>', $path or die "open for write ($path): $!";
print {$out} "Line 1: Hello from Perl\n";
print {$out} "Line 2: Writing to a file is just printing to a filehandle\n";
close $out or die "close ($path): $!";

open my $in, '<', $path or die "open for read ($path): $!";
while (my $line = <$in>) {
  print $line;
}
close $in or die "close ($path): $!";

Expected output (STDOUT):

Line 1: Hello from Perl
Line 2: Writing to a file is just printing to a filehandle

Writing APIs: print, say, printf, syswrite

  • print: writes strings exactly as provided (no newline added automatically).
  • say: like print but adds a newline; enable with use feature 'say'; (or use v5.10; and above).
  • printf: formatted output, like C printf.
  • syswrite: unbuffered, low-level writes; you handle partial writes and offsets.

For most scripts, print/say/printf are correct and simplest. Use syswrite when you need precise control (binary protocols, performance tuning, non-blocking IO).

Example 2: Append to a file (and why it matters) (runnable)

Appending is ideal for logs. The key difference is the mode: '>>' does not truncate.

#!/usr/bin/env perl
use strict;
use warnings;

my $path = 'example2.log';

# Start fresh for the demo
open my $start, '>', $path or die "open ($path): $!";
print {$start} "[init] log started\n";
close $start or die "close ($path): $!";

# Append two lines
open my $app, '>>', $path or die "open for append ($path): $!";
print {$app} "[info] first appended line\n";
print {$app} "[info] second appended line\n";
close $app or die "close ($path): $!";

# Show file content
open my $in, '<', $path or die "open for read ($path): $!";
print while (<$in>);
close $in or die "close ($path): $!";

Expected output (STDOUT):

[init] log started
[info] first appended line
[info] second appended line

Encoding and text vs binary

Perl distinguishes bytes (raw) from characters (text). Files on disk are bytes, while your Perl strings may represent characters. If you write non-ASCII text (accented characters, emojis, many languages), you should choose an encoding layer explicitly (UTF-8 is common):

open my $fh, '>:encoding(UTF-8)', $path or die $!;
print {$fh} "café\n";
close $fh or die $!;

If you do not set an encoding layer, behavior can vary depending on your data and environment, and you may see warnings about wide characters, or you may write mojibake (garbled text). For binary files (images, zip, protocol frames), do not use :encoding; instead use :raw.

Example 3: Safer writing with UTF-8 and an atomic replace pattern (runnable)

When writing configuration or data files that must not be left half-written (power loss, crash, deploy kill), a common best practice is: write to a temporary file in the same directory, close it, then rename it over the destination. On most systems, rename within the same filesystem is atomic.

#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw(tempfile);

my $path = 'example3.txt';

my ($tmp_fh, $tmp_path) = tempfile('example3.tmp.XXXX', UNLINK => 0);

# Write UTF-8 text safely to the temp file
binmode($tmp_fh, ':encoding(UTF-8)') or die "binmode: $!";
print {$tmp_fh} "Name: café\n";
print {$tmp_fh} "Status: OK\n";
close $tmp_fh or die "close temp: $!";

# Atomically replace the target
rename $tmp_path, $path or die "rename($tmp_path -> $path): $!";

# Show final content
open my $in, '<:encoding(UTF-8)', $path or die "open for read ($path): $!";
print while (<$in>);
close $in or die "close ($path): $!";

Expected output (STDOUT):

Name: café
Status: OK

Best practices

  • Use three-argument open: open my $fh, '>', $path, not old two-argument forms.
  • Use lexical filehandles: my $fh keeps scope clear and avoids globals.
  • Check errors on open and close: close can fail (buffer flush, NFS, full disk).
  • Prefer autodie in scripts: it turns common IO failures into exceptions automatically.
  • Choose encoding intentionally: '>:encoding(UTF-8)' for text; '>:raw' for bytes.
  • Be explicit about truncation: choose '>' vs '>>' consciously.
  • Use file locking when multiple writers exist: flock prevents interleaving/overwrites in many cases.
  • Write atomically for critical files: temp + rename avoids partially written destinations.

Quick note on autodie

In many scripts you can replace repetitive or die checks with:

use autodie;
open my $fh, '>', $path;
print {$fh} "...";
close $fh;

This does not remove the need to think about modes, encoding, or atomicity, but it reduces boilerplate.

Pitfalls and how to avoid them

  • Accidental truncation: opening with '>' wipes the file immediately; use '>>' for logs.
  • Not checking close: errors can occur on flush; always check when data matters.
  • Using two-argument open with user input: can be parsed as a pipe; use three arguments.
  • Encoding warnings / mojibake: set :encoding(UTF-8) for text and be consistent on read/write.
  • Concurrent writers: without flock, two processes can interleave lines or overwrite; lock if needed.
  • Buffered output surprises: output may not hit disk immediately; call close or enable autoflush for interactive logs.
  • Permissions and umask: file creation may fail or be unreadable; handle $! and know your runtime user.

When to use flock (briefly)

If multiple processes/threads might write the same file, you often want:

use Fcntl qw(:flock);
open my $fh, '>>', $path or die $!;
flock($fh, LOCK_EX) or die $!;
print {$fh} "one complete record\n";
close $fh or die $!;

This reduces garbled logs, but note that locking behavior can vary on network filesystems.

Summary

To write to a file in Perl, use a lexical filehandle and the three-argument open, pick the right mode ('>' to overwrite, '>>' to append), write with print/say/printf, and always handle errors on open and close. For real-world robustness, be explicit about encoding, consider locks for concurrent writers, and use atomic replace for important files.

Verified Code

Executed in a sandbox to capture real output. • 24ms

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
Line 1: Hello from Perl
Line 2: Writing to a file is just printing to a filehandle
STDERR
(empty)

Was this helpful?

Related Questions