text-processing intermediate

How to read last N lines of a file in Perl?

Question

How to read last N lines of a file in Perl?

Reading the last N lines of a file in Perl is a common task, often referred to as "tailing" a file. Unlike reading the whole file into memory and then picking the last few lines, it’s more efficient to approach this with minimal memory usage—especially for large files.

Common Approaches to Reading Last N Lines in Perl

  • Read entire file into an array and slice: Simple but inefficient for large files.
  • Seek from the end and read backwards: More complex but memory-efficient.
  • Using external commands (like tail) via backticks: Quick but not portable or pure Perl.

Below is a pure Perl approach that reads from the end of the file in blocks, collecting lines until it has at least N lines. This method avoids reading the entire file into memory and is fairly efficient.

Perl Code Example: Reading Last N Lines Efficiently

use strict;
use warnings;

# Parameters
my $filename = 'example.txt';  # Change to your filename
my $n = 10;                    # Number of last lines to read

# Open file for reading
open my $fh, '<', $filename or die "Cannot open '$filename': $!";

# Seek to the end of file
seek $fh, 0, 2 or die "Cannot seek in '$filename': $!";

# Position starts at end
my $pos = tell($fh);
my $buffer = '';
my @lines;

# Read backwards by chunks
my $chunk_size = 4096;

while (@lines <= $n && $pos > 0) {
    # Calculate how far to go back (don't go before start)
    my $read_size = $pos > $chunk_size ? $chunk_size : $pos;
    $pos -= $read_size;
    
    # Seek back by $read_size bytes
    seek $fh, $pos, 0 or die "Cannot seek to $pos: $!";
    
    # Read chunk
    my $data;
    read $fh, $data, $read_size;
    
    # Prepend to buffer
    $buffer = $data . $buffer;
    
    # Split lines
    @lines = split /\n/, $buffer;
}

# We might have more lines than needed, so get just last N
my @last_n = @lines[-$n..-1];

# Print last N lines
print join("\n", @last_n), "\n";

Explanation and Perl-specific Details

  • open with < opens the file for reading.
  • seek and tell are used to move the filehandle pointer and get its current position, respectively.
  • We read in fixed-size chunks ($chunk_size) from the end of the file backwards, accumulating the data until we have at least N lines.
  • split /\n/ breaks the buffer into lines — note this is sensitive to newline conventions (this works fine with Unix-style \n line endings).
  • We use negative array slicing @lines[-$n..-1] to pick the last N lines.
  • Perl's flexibility with context (“TMTOWTDI” - There's More Than One Way To Do It) allows other strategies but this is a readable and efficient approach.

Common Pitfalls

  • Line endings: This method assumes \n as line ending; Windows-style \r\n handling may require adjustement.
  • Files without newline at end: If the last line lacks a newline, splitting still works but results might differ.
  • Very small files: If the file has fewer than N lines, it simply prints all lines.
  • Binary or large files: This technique assumes text files.

This method works well on Perl 5.8+ (common in most modern Perl versions), relying only on core features.

If you need a simple quick and dirty method and are sure files are small, just reading all lines into an array is possible:

open my $fh, '<', $filename or die $!;
my @lines = <$fh>;
my @last_n = @lines[-$n..-1];
print @last_n;

But beware this loads entire file into memory.

Hopefully, the backward read approach above gives you an efficient, pure-Perl way to get the last N lines without needing external commands or excessive memory.

Verified Code

Executed in a sandbox to capture real output. • v5.34.1 • 7ms

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT








Hello, core Perl file writing!
This works in any environment.
STDERR
Use of uninitialized value $last_n[0] in join or string at - line 45.
Use of uninitialized value $last_n[1] in join or string at - line 45.
Use of uninitialized value $last_n[2] in join or string at - line 45.
Use of uninitialized value $last_n[3] in join or string at - line 45.
Use of uninitialized value $last_n[4] in join or string at - line 45.
Use of uninitialized value $last_n[5] in join or string at - line 45.
Use of uninitialized value $last_n[6] in join or string at - line 45.
Use of uninitialized value $last_n[7] in join or string at - line 45.

Was this helpful?

Related Questions