Как да изтриете последните 5 реда от файл

Имам няколко команди за отпечатване на текст във файл с помощта на perl. По време на тези print команди имам оператор if, който трябва да изтрие последните 5 реда от файла, в който пиша в момента, ако операторът е верен. Броят на редовете за изтриване винаги ще бъде 5.

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

person charles hendry    schedule 17.02.2012    source източник
comment
вижте този въпрос: stackoverflow.com/questions/345513/   -  person asf107    schedule 17.02.2012
comment
От Stack Overflow и официални често задавани въпроси за Perl: Как да променя, изтрия или вмъкна ред във файл или да добавя към началото на файл в Perl?   -  person daxim    schedule 17.02.2012


Отговори (6)


Можете да използвате Tie::File:

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

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

Можете да използвате същия масив при печат, но вместо това използвайте push:

push @array, "line of text";
person TLP    schedule 17.02.2012
comment
Щом прочетох заглавието си помислих Tie::File FTW! Ти ме изпревари. +1 - person Joel Berger; 17.02.2012

Само очевидни начини, за които се сещам:

  1. Заключете файл, сканиране назад, за да намерите позиция и да използвате truncate.
  2. Не печатайте директно във файла, преминете през буфер с дължина поне 5 реда и изрежете буфера.
  3. Отпечатайте маркер, който означава „игнорирайте последните пет реда“. Обработете всичките си файлове, преди да ги прочетете с буфер, както в #2

Всички са доста неудобни, но страхувам се, че това е природата на плоските файлове.

HTH

person Richard Huxton    schedule 17.02.2012
comment
Не че това са лоши предложения, но Tie::File има вградената магия. - person Joel Berger; 17.02.2012
comment
@Joel Berger, не е така. Tie::File чете файла от самото начало, така че определено ще бъде много по-бавно от (1) за големи файлове. Да не говорим за цялата памет, която ще използва от създаването на индекс на всеки ред във файла. - person ikegami; 18.02.2012
comment
@ikegami, прав си за #1, че е по-бърз (за големи файлове). Всичко, което всъщност имах предвид, беше, че Tie::File ще свърши по-голямата част от работата, а не че ще бъде по-ефективно - person Joel Berger; 18.02.2012

Като алтернатива отпечатайте целия файл с изключение на последните 5 реда:

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: $!";
person DVK    schedule 17.02.2012
comment
Това е номер 2 от отговора на Ричард Хъкстън - person DVK; 17.02.2012
comment
$index = 0 if 5 == $index може също да се запише $index %= 5, като се приемат стъпки от едно. - person TLP; 17.02.2012
comment
Това са много странни отворени разговори. open връща недефинирана истинска стойност при успех и нула при неуспех - нито една от двете не е полезна като манипулатори на файлове. $file е мястото, където трябва да бъде манипулаторът на файла, но той не е деклариран и използвате една и съща променлива два пъти. Също така $filename.new изисква двойни кавички около него или няма да се компилира. - person Borodin; 17.02.2012
comment
@Borodin - хълцане на мозъка - все още не е пил кофеин. Ти си абсолютно прав. - person DVK; 17.02.2012

File::ReadBackwards+truncate е най-бързият за големи файлове и вероятно толкова бърз, колкото всичко друго за кратки файлове.

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 е най-бавният и използва голямо количество памет. Избягвайте това решение.

person ikegami    schedule 17.02.2012

можете да опитате нещо подобно:

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

или дори съкратено:

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

person    schedule
comment
@TLP: head: unknown option -- - и usage: head [-count | -n count] [file ...]. Това не е съвместимо с POSIX. - person tchrist; 18.02.2012
comment
@TLP И какво? Това не е POSIX. Не е стандартно. Това е прозаичен местен вариант. Имам поне 3 системи, където се проваля ужасно. Не трябва да очаквате други хора да изпълняват нестандартен софтуер. Хората, които живеят в оранжерията на Linux, не знаят какъв е реалният свят навън. - person tchrist; 18.02.2012
comment
Всеки ден бих победил head -n -5 над tac в състезание за преносимост. - person socket puppet; 18.02.2012
comment
@socketpocket Просто псевдоним tac на perl -e 'print reverse <>' — винаги го правя. :) Честно казано, вероятно имах това десетилетия преди tac (1) да започне да доставя. По дяволите, имах tac преди Perl, за бога! - person tchrist; 18.02.2012
comment
@tchrist Мисля, че си малко негативен тук. Въпросът е, че е по-добре да видите дали head може да се използва във вашата система, отколкото да обърнете файла, да го отпечатате и да го обърнете отново. - person TLP; 18.02.2012
comment
@TLP: ако възнамерявате да предложите стандартни Unix инструменти, предоставете версия, която не използва дълги опции (както направи socket puppet). Дългите опции са разширение на GNU и не са преносими. - person ninjalj; 19.02.2012