user391986
user391986

Reputation: 30886

get last few lines of file stored in variable

How could I get the last few lines of a file that is stored in a variable? On linux I would use the tail command if it was in a file.

1) How can I do this in perl if the data is in a file?
2) How can I do this if the content of the file is in a variable?

Upvotes: 3

Views: 6434

Answers (5)

Adrian
Adrian

Reputation: 10911

A lot has already been stated on the file side, but if it's already in a string, you can use the following regex:

my ($lines) = $str ~= /
(
 (?:
  (?:(?<=^)|(?<=\n)) # match beginning of line (separated due to variable lookbehind limitation) 
  [^\n]*+            # match the line
  (?:\n|$)           # match the end of the line
 ){0,5}+             # match at least 0 and at most 5 lines
)$                   # match must be from end of the string
/sx                  # s = treat string as single line
                     # x = allow whitespace and comments

This runs extremely fast. Benchmarking shows between 40-90% faster compared to the split/join method (variable due to current load on machine). This is presumably due to less memory manipulations. Something you might want to consider if speed is essential. Otherwise, it's just interesting.

Upvotes: 0

Pierre D
Pierre D

Reputation: 26201

I know this is an old question, but I found it while looking for a way to search for a pattern in the first and last k lines of a file.

For the tail part, in addition to seek (if the file is seekable), it saves some memory to use a rotating buffer, as follows (returns the last k lines, or less if fewer than $k are available):

my $i = 0; my @a;
while (<$fh>) {
    $a[$i++ % $k] = $_;
}
my @tail = splice @a,0,$i % $k;
splice @a,@a,0,@tail;
return @a;

Upvotes: 0

DrHyde
DrHyde

Reputation: 1695

The File::ReadBackwards module on the CPAN is probably what you want. You can use it thus. This will print the last three lines in the file:

use File::ReadBackwards
my $bw = File::ReadBackwards->new("some_file");
print reverse map { $bw->readline() } (1 .. 3);

Internally, it seek()s to near the end of the file and looks for line endings, so it should be fairly efficient with memory, even with very big files.

Upvotes: 4

mob
mob

Reputation: 118595

To read the end of a file, seek near the end of the file and begin reading. For example,

open my $fh, '<', $file;
seek $fh, -1000, 2;
my @lines = <$fh>;
close $fh;

print "Last 5 lines of $file are: ", @lines[-5 .. -1];

Depending on what is in the file or how many lines you want to look at, you may want to use a different magic number than -1000 above.

You could do something similar with a variable, either

open my $fh, '<', \$the_variable;
seek $fh, -1000, 2;

or just

open my $fh, '<', \substr($the_variable, -1000);

will give you an I/O handle that produces the last 1000 characters in $the_variable.

Upvotes: 5

Stuart Watt
Stuart Watt

Reputation: 5391

To some extent, that depends how big the file is, and how many lines you want. If it is going to be very big you need to be careful, because reading it all into memory will take a lot longer than just reading the last part of the file.

If it is small. the easiest way is probably to File::Slurp it into memory, split by record delimiters, and keep the last n records. In effect, something like:

# first line if not yet in a string
my $string = File::Slurp::read_file($filename);
my @lines = split(/\n/, $string);
print join("\n", @lines[-10..-1])

If it is large, too large to find into memory, you might be better to use file system operations directly. When I did this, I opened the file and used seek() and read the last 4k or so of the file, and repeated backwards until I had enough data to get the number of records I needed.

Not a detailed answer, but the question could be a touch more specific.

Upvotes: 3

Related Questions