Skip to content

Perl File I/O

File I/O (Input/Output) in Perl involves associating a filehandle with an external entity (usually a file) and then using Perl’s operators and functions to read from or write to that entity.

A filehandle is an internal Perl name that connects your script to a file or device. Modern Perl uses lexical filehandles (variables storing the filehandle) for better scope control and error handling.

Perl predefines three standard filehandles: STDIN (standard input), STDOUT (standard output), and STDERR (standard error).

The primary function for opening files is open(). For lower-level control, sysopen() can be used.

Modern open() Syntax (Three-Argument Form):

open(my $fh, '<', $filename) or die "Cannot open $filename for reading: $!";
open(my $fh, '>', $filename) or die "Cannot open $filename for writing: $!";
open(my $fh, '>>', $filename) or die "Cannot open $filename for appending: $!";

$fh is a lexical filehandle. $filename is the path to the file. The second argument is the mode. $! contains the system error message if open() fails.

It’s highly recommended to add use strict; and use warnings; at the beginning of your scripts for robust error checking.

Here’s a table of common modes for the three-argument open():

Mode StringDescription
’<‘Open for reading (read-only). File must exist.
’>‘Open for writing. Creates file if it doesn’t exist, truncates (empties) if it does.
’>>‘Open for appending. Creates file if it doesn’t exist. Writes will be added to the end.
’+<‘Open for reading and writing. File must exist.
’+>‘Open for reading and writing. Creates file if it doesn’t exist, truncates if it does.
’+>>‘Open for reading and appending. Creates file if it doesn’t exist. Writes will be added to the end.

For handling character encodings (e.g., UTF-8), you can add layers to the mode:

open(my $fh, '<:encoding(UTF-8)', $filename) or die "...: $!";
open(my $fh, '>:encoding(UTF-8)', $filename) or die "...: $!";

Example: Reading from a file

#!/usr/bin/perl
use strict;
use warnings;
my $filename = 'file.txt';
open(my $fh, '<', $filename) or die "Could not open file '$filename': $!";
print "Reading from $filename:\n";
while (my $row = <$fh>) {
chomp $row; # Remove newline character
print "Line: $row\n";
}
close($fh);

Example: Writing to a file (truncates if exists)

#!/usr/bin/perl
use strict;
use warnings;
my $filename = 'output.txt';
open(my $fh, '>', $filename) or die "Could not open file '$filename' for writing: $!";
print $fh "Hello, file!\n";
print $fh "This is a new line.\n";
close($fh);
print "Data written to $filename\n";

sysopen() provides lower-level access to the system’s open() call. It requires the Fcntl module for constants.

sysopen(my $fh, $filename, $flags) or die "sysopen $filename: $!";
sysopen(my $fh, $filename, $flags, $perms) or die "sysopen $filename: $!";

$flags are constants like O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_APPEND (imported from Fcntl). $perms specifies permissions if the file is created (e.g., 0666).

Example using sysopen:

#!/usr/bin/perl
use strict;
use warnings;
use Fcntl qw(:DEFAULT O_CREAT O_TRUNC); # Import constants
my $filename = 'sysopened.txt';
my $permissions = 0644;
sysopen(my $fh, $filename, O_WRONLY | O_CREAT | O_TRUNC, $permissions)
or die "Cannot sysopen $filename: $!";
print $fh "Written via sysopen.\n";
close($fh);
print "Data written to $filename using sysopen.\n";

Common Fcntl constants for sysopen mode:

ConstantDescription
O_RDONLYRead Only
O_WRONLYWrite Only
O_RDWRRead and Write
O_CREATCreate the file if it doesn’t exist
O_APPENDAppend to the file
O_TRUNCTruncate the file (empty it)
O_EXCLUsed with O_CREAT, fails if file already exists
O_NONBLOCKNon-blocking I/O

The close() function flushes buffers and disassociates the filehandle from the file.

close($fh) or die "Could not close filehandle: $!";

It’s good practice to check the return value of close(), especially for output filehandles, as errors during flushing can occur.

Once a filehandle is open, you can read from or write to it.

The primary way to read from a filehandle is the diamond operator <$fh>. In scalar context, it reads one line. In list context, it reads all remaining lines.

#!/usr/bin/perl
use strict;
use warnings;
# Assuming 'input.txt' exists and has content
my $filename_in = 'input.txt';
open(my $in_fh, '<', $filename_in) or die "Cannot open $filename_in: $!";
# Scalar context: read one line
my $first_line = <$in_fh>;
chomp $first_line if defined $first_line;
print "First line: $first_line\n";
# List context: read all remaining lines into an array
my @remaining_lines = <$in_fh>;
close($in_fh);
print "Remaining lines:\n";
for my $line (@remaining_lines) {
chomp $line;
print "- $line\n";
}

<STDIN> reads a line from standard input.

getc($fh) reads a single character from the filehandle. Returns undef on error or end-of-file.

read($fh, my $buffer, $length, $offset) reads $length bytes into $buffer. If $offset is specified, it reads into $buffer starting at that offset. Useful for binary data.

print $fh LIST writes the items in LIST to the specified filehandle. If $fh is omitted, prints to STDOUT (or the currently selected default output handle).

#!/usr/bin/perl
use strict;
use warnings;
my $filename_out = 'greetings.txt';
open(my $out_fh, '>', $filename_out) or die "Cannot open $filename_out: $!";
print $out_fh "Hello, world!\n"; # Write to file
print "Message written to $filename_out\n"; # Write to STDOUT
close($out_fh);

A common task is copying content from one file to another.

#!/usr/bin/perl
use strict;
use warnings;
my $source_file = 'source.txt'; # Assume this file exists
my $dest_file = 'destination.txt';
open(my $source_fh, '<', $source_file)
or die "Cannot open source file '$source_file': $!";
open(my $dest_fh, '>', $dest_file)
or die "Cannot open destination file '$dest_file': $!";
# Copy line by line
while (my $line = <$source_fh>) {
print $dest_fh $line;
}
close($source_fh);
close($dest_fh) or die "Error closing destination file '$dest_file': $!";
print "File '$source_file' copied to '$dest_file'\n";

For binary files or larger files, consider reading in chunks using read() for efficiency.

The rename() function renames a file.

#!/usr/bin/perl
use strict;
use warnings;
my $old_name = 'oldfile.txt';
my $new_name = 'newfile.txt';
# Create a dummy old file for the example
open(my $fh, '>', $old_name) or die "Could not create $old_name: $!";
print $fh "This is the old file.\n";
close($fh);
if (rename($old_name, $new_name)) {
print "File '$old_name' renamed to '$new_name' successfully.\n";
} else {
warn "Could not rename '$old_name' to '$new_name': $!";
}

The unlink() function deletes one or more files.

#!/usr/bin/perl
use strict;
use warnings;
my $file_to_delete = 'tempfile.txt';
# Create a dummy file for the example
open(my $fh, '>', $file_to_delete) or die "Could not create $file_to_delete: $!";
print $fh "This file will be deleted.\n";
close($fh);
if (unlink($file_to_delete)) {
print "File '$file_to_delete' deleted successfully.\n";
} else {
warn "Could not delete '$file_to_delete': $!";
}

tell() returns the current position (in bytes) of the file pointer. seek() moves the file pointer to a specific position.

my $current_position = tell($fh);
seek($fh, $position, $whence);

$position is the offset. $whence determines how $position is interpreted:

  • 0: Seek from the beginning of the file (SEEK_SET).
  • 1: Seek from the current position (SEEK_CUR).
  • 2: Seek from the end of the file (SEEK_END).

These constants (SEEK_SET, SEEK_CUR, SEEK_END) can be imported from the Fcntl or IO::Seekable modules. Using the numeric values 0, 1, 2 is also common.

# Example: Seek to byte 10 from the beginning
seek($fh, 10, 0) or die "Seek failed: $!";

Perl provides file test operators (e.g., -e, -f, -d) to check various attributes of a file or directory.

#!/usr/bin/perl
use strict;
use warnings;
my $file = 'example.txt'; # Or any file/directory path
# Create a dummy file for testing
open (my $fh, '>', $file) or die "Could not create $file: $!";
print $fh "Some content\n";
close ($fh);
if (-e $file) {
print "'$file' exists.\n";
print "'$file' is a plain file.\n" if (-f _); # _ refers to the last stat'd filehandle
print "'$file' is a directory.\n" if (-d _);
print "'$file' is readable.\n" if (-r _);
print "'$file' is writable.\n" if (-w _);
print "'$file' is executable.\n" if (-x _);
print "'$file' size is: ", -s _, " bytes.\n" if (-s _);
print "'$file' is a text file.\n" if (-T _);
print "'$file' is a binary file.\n" if (-B _);
} else {
print "'$file' does not exist.\n";
}
unlink $file; # Clean up dummy file

Common File Test Operators:

OperatorDefinition
-eFile or directory exists.
-fIs a plain file.
-dIs a directory.
-rIs readable by effective UID/GID.
-wIs writable by effective UID/GID.
-xIs executable by effective UID/GID.
-sFile size in bytes (returns size, or undef if file doesn’t exist).
-zFile has zero size (is empty).
-lIs a symbolic link.
-TFile looks like a text file.
-BFile looks like a binary file.
-MAge of file (modification time - script start time) in days.
-AAge of file (access time - script start time) in days.
-CAge of file (inode change time - script start time) in days.

For more advanced file and directory manipulation, consider CPAN modules like Path::Tiny or File::Spec.