1

I know $. shows the line number when $/ is set to "\n".

I wanted to emulate the Unix tail command in Perl and print the last 10 lines from a file but $. didn't work. If the file contains 14 lines it starts from 15 in the next loop.

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

my $i;

open my $fh, '<', $ARGV[0] or die "unable to open file $ARGV[0] :$! \n";
do { local $.; $i = $. } while (<$fh>);
seek $fh, 0, 0;

if ($i > 10) {
    $i = $i - 10;
    print "$i \n";
    while (<$fh>) {

        #local $.;# tried doesn't work
        #undef $.; #tried doesn't work

        print "$. $_" if ($. > $i);
    }
}
else {
    print "$_" while (<$fh>);
}

close($fh);

I want to reset $. so it can be used usefully in next loop.

Borodin
  • 126,100
  • 9
  • 70
  • 144
Arunesh Singh
  • 3,489
  • 18
  • 26
  • 1
    Not an answer to your question but an alternative solution to your initial problem. In order to print last ten lines you can use this: `perl -nle 'push @tail, $_; shift @tail if @tail > 10; END {print foreach (@tail)}' input.txt ` – Dmitry Egorov Jun 10 '15 at 07:47
  • This discussion may help you : http://stackoverflow.com/questions/6036244/perl-is-there-a-built-in-function-to-clear-all-variable-values – chipou Jun 10 '15 at 07:48
  • Here is another implementation of `tail` in Perl 5: https://metacpan.org/source/BDFOY/PerlPowerTools-1.006/bin/tail#L142 – dolmen Jun 10 '15 at 15:16

2 Answers2

5

Using local with $. does something else than you think:

Localizing $. will not localize the filehandle's line count. Instead, it will localize perl's notion of which filehandle $. is currently aliased to.

$. is not read-only, it can be assigned to normally.

1 while <$fh>;
my $i = $.;
seek $fh, $. = 0, 0;
choroba
  • 231,213
  • 25
  • 204
  • 289
0

You must reopen the file handle. Otherwise, as you have found, the line number just continues to increment

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

my ($filename) = @ARGV;

my $num_lines;
open my $fh, '<', $filename or die qq{Unable to open file "$filename" for input: $!\n};
++$num_lines while <$fh>;

open $fh, '<', $filename or die qq{Unable to open file "$filename" for input: $!\n};

print "$num_lines lines\n";
while ( <$fh> ) {
    print "$. $_" if $. > $num_lines - 10;
}

Here's a neater way

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

my ($filename) = @ARGV;

my @lines;
open my $fh, '<', $filename or die qq{Unable to open file "$filename" for input: $!\n};

while ( <$fh> ) {
  push @lines, $_;
  shift @lines while @lines > 10;
}

print @lines;
Borodin
  • 126,100
  • 9
  • 70
  • 144