4

I am trying to create a Perl script that outputs the last two lines of a file. My current script accomplishes what I want, except that it reads the first two lines rather than the last two.

use strict;
use warnings;

my $file = 'test.txt';
open my $info, $file or die "Could not open $file: $!";

while( my $line = <$info>)  {   
    print $line;    
    last if $. == 2;
}

close $info;

What must I alter so that the script reads the last two lines rather than the first two?

Miller
  • 34,962
  • 4
  • 39
  • 60
Opc
  • 43
  • 2

2 Answers2

15

This is harder than printing the first lines! Here are a few options, ranging from least to most clever:

Just use tail

Because who actually needs a perl script to print the last two lines of a file?

Read all the lines from the file into a list, and print the last two.

print do {
    open my $info, $file or die ...;
    (<$info>)[-2, -1];
};

Read all the lines from the file, only remembering the last two.

open my $info, $file or die ...;
my @lines;
while (my $line = <$info>) {
  shift @lines if @lines == 2;
  push @lines, $line;
}
print @lines;

Read the file backwards, looking for line-endings; when two are found, read forwards from that point.

This is the most memory efficient, especially if a "line" is worryingly long, but complicated. However, there's a module for it: File::ReadBackwards. It lets you do:

use File::ReadBackwards;
my $info = File::ReadBackwards->new($file) or die ...;
my $last = $info->readline;
my $second_last = $info->readline;
print $second_last, $last;
Miller
  • 34,962
  • 4
  • 39
  • 60
hobbs
  • 223,387
  • 19
  • 210
  • 288
  • There is also PerlIO::reverse and the tac command for reading the file backwards http://stackoverflow.com/q/3221008 – David L. Aug 30 '14 at 04:11
  • And of course `IO::All` has the 'backwards' modifier, as well as 'tail'. – DavidO Aug 30 '14 at 06:36
  • To put code to your tail suggestion, the following pipes the last 2 lines through the filehandle: `open my $info, "tail -2 $file |" or die ...;`. – Chris Aug 30 '14 at 22:36
  • +1 for all the options. There is `tac` which does the job of reversing the file. `tac file | head -n2 | tac` – jaypal singh Sep 05 '14 at 13:55
1

You can use Tie::File or File::ReadBackwards.

The code looks like this.

Using Tie::File

use strict;
use warnings;

use Tie::File;

my $file = 'test.txt';

tie my @file, 'Tie::File', $file or die $!;

print "\nUsing Tie::File\n";
print "$_\n" for @file[-2,-1];

Using File::Readbackwards

use strict;
use warnings;

use File::Readbackwards;

my $file = 'test.txt';

my $backwards = File::ReadBackwards->new($file) or die $!;

my @last_two;
while (defined(my $line = $backwards->readline)) {
   unshift @last_two, $line;
   last if @last_two >= 2;
}

print "\nUsing File::ReadBackwards\n";
print @last_two;
Borodin
  • 126,100
  • 9
  • 70
  • 144