我有一个巨大的文本文件(~1.5GB),其中有许多行以“.Ends”结尾。
\n我需要一个 Linux oneliner (perl\\ awk\\ sed) 来查找文件中出现的最后一个位置 '.Ends' 并在其前面添加几行。
我尝试使用tac两次,并偶然发现了我的 perl:
当我使用:
\n tac ../../test | perl -pi -e 'BEGIN {$flag = 1} if ($flag==1 && /.Ends/) {$flag = 0 ; print "someline\\n"}' | tac
\n它首先打印“someline\\n”,然后只打印 .Ends\n结果是:
\n\xe2\x80\xa6
\n.Ends
\nsomeline
当我使用:
\n tac ../../test | perl -e 'BEGIN {$flag = 1} print ; if ($flag==1 && /.Ends/) {$flag = 0 ; print "someline\\n"}' | tac
\n它不会\xe2\x80\x99t 打印任何内容。
当我使用时:
\n tac ../../test | perl -p -e 'BEGIN {$flag = 1} print $_ ; if ($flag==1 && /.Ends/) {$flag = 0 ; print "someline\\n"}' | tac
\n它将所有内容打印两次:
\n\xe2\x80\xa6
\n.Ends
\nsomeline
\n.Ends
有没有一种顺利的方法来执行此编辑?
\n不必遵循我的解决方案方向,我不挑剔...
\n奖励 - 如果这些行可以来自不同的文件,那就太好了(但实际上不是必须的)
编辑
\n测试输入文件:
gla2 \nfla3 \ndla4 \nrfa5 \n.Ends\nshu\nsha\nshe\n.Ends\nres\npes\nges\n.Ends \n--->\n...\npes\nges\nsomeline\n.Ends \n# * some irrelevant junk * #\nRun Code Online (Sandbox Code Playgroud)\n
如果该短语的最后一个实例在文件中足够远,那么从后面处理文件可以极大地提高性能,例如使用File::ReadBackwards。事实上,这种方法在任何情况下都有帮助,因为我们只需要阅读绝对必要的内容(该短语的最后一个实例之后的其余部分),并且一次。
由于您需要在最后一个标记之前将其他文本添加到文件中,因此我们必须复制其余文本,以便能够在添加后将其放回原处。
use warnings;
use strict;
use feature 'say';
use Path::Tiny;
use File::ReadBackwards;
my $file = shift // die "Usage: $0 file\n";
my $bw = File::ReadBackwards->new($file);
my @rest_after_marker;
while ( my $line = $bw->readline ) {
unshift @rest_after_marker, $line;
last if $line =~ /\.Ends/;
}
# Position after which to add text and copy back the rest
my $pos = $bw->tell;
$bw->close;
open my $fh, '+<', $file or die $!;
seek $fh, $pos, 0;
truncate $fh, $pos;
print $fh $_ for path("add.txt")->slurp, @rest_after_marker;
Run Code Online (Sandbox Code Playgroud)
在最后一个文本之前添加的新文本.Ends可能位于文件中add.txt。
问题仍然是最后一个标记之后还有多少文件.Ends?我们将所有内容复制到内存中,以便能够将其写回。如果太多,请将其复制到临时文件而不是内存中,然后从那里使用它,最后删除该文件。
输入:
$ cat test.dat
dla4
.Ends
she
.Ends
res
.Ends
abc
$ cat new.dat
newline 111
newline 222
Run Code Online (Sandbox Code Playgroud)
awkOP 的方法坚持一个想法tac | <process> | tac:
$ tac test.dat | awk -v new_dat="new.dat" '1;/\.Ends/ && !(seen++) {system("tac " new_dat)}' | tac
dla4
.Ends
she
.Ends
res
newline 111
newline 222
.Ends
abc
Run Code Online (Sandbox Code Playgroud)
另一个awk想法是tac用输入文件的双通道替换双调用:
$ awk -v new_dat="new.dat" 'FNR==NR { if ($0 ~ /\.Ends/) lastline=FNR; next} FNR==lastline { system("cat "new_dat) }; 1' test.dat test.dat
dla4
.Ends
she
.Ends
res
newline 111
newline 222
.Ends
abc
Run Code Online (Sandbox Code Playgroud)
笔记:
test.dat)