charles hendry
charles hendry

Reputation: 1740

How to delete the last 5 lines of a file

I have several commands printing text to a file using perl. During these print commands I have an if statement which should delete the last 5 lines of the file I am currently writing to if the statement is true. The number of lines to delete will always be 5.

if ($exists == 0) {
  print(OUTPUT ???) # this should remove the last 5 lines
}

Upvotes: 0

Views: 2491

Answers (6)

ashraf
ashraf

Reputation: 557

you can try something like this:

open FILE, "<", 'filename';
if ($exists == 0){
 @lines = <FILE>;
 $newLastLine = $#lines - 5;   
 @print = @lines[0 .. $newLastLine];
 print "@print";
}

or even shortened:

open FILE, "<", 'filename';
@lines = <FILE>;
if ($exists == 0){
 print "@lines[0 .. $#lines-5]";
}

Upvotes: 0

ikegami
ikegami

Reputation: 385789

File::ReadBackwards+truncate is the fastest for large files, and probably as fast as anything else for short files.

use File::ReadBackwards qw( );

my $bfh = File::ReadBackwards->new($qfn)
   or die("Can't read \"$qfn\": $!\n");

$bfh->readline() or last for 1..5;

my $fh = $bfh->get_handle();
truncate($qfn, tell($fh))
   or die $!;

Tie::File is the slowest, and uses a large amount of memory. Avoid that solution.

Upvotes: 1

DVK
DVK

Reputation: 129403

As an alternative, print the whole file except last 5 lines:

open($fh, "<", $filename) or die "can't open $filename for reading: $!";
open($fh_new, ">", "$filename.new") or die "can't open $filename.new: $!";
my $index = 0; # So we can loop over the buffer
my @buffer;
my $counter = 0;
while (<$fh>) {
    if ($counter++ >= 5) {
        print $fh_new $buffer[$index];
    }
    $buffer[$index++] = $_;
    $index = 0 if 5 == $index;
}
close $fh;
close $fh_new;
use File::Copy;
move("$filename.new", $filename) or die "Can not copy $filename.new to $filename: $!";

Upvotes: 1

tchrist
tchrist

Reputation: 80384

$ tac file | perl -ne 'print unless 1 .. 5' | tac > file.tailchopped

Upvotes: 3

Richard Huxton
Richard Huxton

Reputation: 22893

Only obvious ways I can think of:

  1. Lock file, scan backwards to find a position and use truncate.
  2. Don't print to the file directly, go through a buffer that's at least 5 lines long, and trim the buffer.
  3. Print a marker that means "ignore the last five lines". Process all your files before reading them with a buffer as in #2

All are pretty fiddly, but that's the nature of flat files I'm afraid.

HTH

Upvotes: 1

TLP
TLP

Reputation: 67900

You can use Tie::File:

use Tie::File;
tie my @array, 'Tie::File', filename or die $!;

if ($exists == 0) {
    $#array -= 5;
}

You can use the same array when printing, but use push instead:

push @array, "line of text";

Upvotes: 4

Related Questions