我有一個巨大的文本檔案 (~1.5GB),其中有許多行以“.Ends”結尾。
我需要一個 linux oneliner (perl\ awk\ sed) 來找到檔案中最后出現“.Ends”的地方,并在它之前添加幾行。
我嘗試使用tac兩次,并偶然發現了我的 perl:
當我使用時:
tac ../../test | perl -pi -e 'BEGIN {$flag = 1} if ($flag==1 && /.Ends/) {$flag = 0 ; print "someline\n"}' | tac
它首先列印“someline\n”然后只列印 .Ends 結果是:
...
.Ends
someline
當我使用時:
tac ../../test | perl -e 'BEGIN {$flag = 1} print ; if ($flag==1 && /.Ends/) {$flag = 0 ; print "someline\n"}' | tac
它不列印任何東西。
當我使用時:
tac ../../test | perl -p -e 'BEGIN {$flag = 1} print $_ ; if ($flag==1 && /.Ends/) {$flag = 0 ; print "someline\n"}' | tac
它列印所有內容兩次:
...
.Ends
someline
.Ends
是否有一種流暢的方式來執行此編輯?
不必與我的解決方案方向一致,我不挑剔......
獎金 - 如果這些行可以來自不同的檔案,那就太好了(但真的不是必須的)
編輯
測驗輸入檔案:
gla2
fla3
dla4
rfa5
.Ends
shu
sha
she
.Ends
res
pes
ges
.Ends
--->
...
pes
ges
someline
.Ends
# * some irrelevant junk * #
uj5u.com熱心網友回復:
假設該短語的最后一個實體在檔案的最下方,它可以極大地幫助性能從后面處理檔案,例如使用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;
在最后一個之前添加的新文本.Ends大概在一個檔案中add.txt。
.Ends問題仍然是最后一個標記之后有多少檔案?我們將所有內容復制到記憶體中,以便能夠將其寫回。如果太多,將它復制到一個臨時檔案而不是記憶體,然后從那里使用它并洗掉該檔案。
uj5u.com熱心網友回復:
使用 GNU sed,-i.bak將創建一個帶有.bak擴展名的備份檔案,同時就地保存原始檔案
$ sed -Ezi.bak 's/(.*)(\.Ends)/\1newline\nnewline\n\2/' input_file
$ cat input_file
gla2
fla3
dla4
rfa5
.Ends
shu
sha
she
.Ends
res
pes
ges
.Ends
--->
...
pes
ges
someline
newline
newline
.Ends
uj5u.com熱心網友回復:
使用 Perl,您可以使用Tie::File核心模塊輕松地從末尾讀取檔案,在需要的地方添加行,僅此而已。
它不會將整個檔案讀入記憶體,也不需要在您的檔案中剪切和粘貼。應該很快。
use strict;
use warnings;
use Tie::File;
# Usage: foo.pl target.txt newlines.txt
#
my $file = shift; # file to edit
chomp(my @new = <>); # read new lines via <>
tie my @file, 'Tie::File', $file or die "Cannot tie '$file': $!";
my $index = $#file; # start from last line in file
while ($index >= 0) { # loop over line number
if ($file[$index] =~ /\.Ends/) {
splice @file, $index, 0, @new; # add new lines before
last; # then exit loop
}
$index--; # next line, going backwards
}
更新:
如果您將此代碼復制/粘貼到檔案中,則此代碼可以作為“單行代碼”運行,例如ends_edit.pl,您可以使用
$ perl ends_edit.pl inputfile new.dat
或者,如果您認為將所有代碼作為沒有檔案的“單行代碼”有一些特別之處,您可以將它們全部塞在一起,如下所示:
$ perl -MTie::File -we'$f=shift; chomp(@n=<>); tie @f, 'Tie::File', $f or die $!; for ($i=$#f; $i<=0; $i--) { if ($f[$i] =~ /\.Ends/) { splice @f, $i, 0, @n; last; } }' inputfile new.dat
這讓代碼變得更好了嗎?好吧,我認為不是。您可能已經要求它,但最終它可能不是您真正想要的。
此代碼的設計目標是提高效率、可讀性、處理大檔案以及使用正確的工具來完成作業。如果您想要以簡短為目標撰寫的代碼,您當然應該選擇其他內容。但是你應該記住,越短并不意味著它越好。
uj5u.com熱心網友回復:
輸入:
$ cat test.dat
dla4
.Ends
she
.Ends
res
.Ends
abc
$ cat new.dat
newline 111
newline 222
awk堅持OP方法的一個想法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
用輸入檔案的雙通道替換雙通道的另一個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
筆記:
- 這兩種解決方案都將修改后的資料寫入標準輸出(與 OP 當前代碼所做的相同)
- 這些解決方案都沒有修改原始輸入檔案 (
test.dat)
uj5u.com熱心網友回復:
由于您想從檔案中讀取新行:
$ cat new
foo
bar
etc
$ tac file | awk 'NR==FNR{str=$0 ORS str; next} {print} $0==".Ends"{printf "%s", str; str=""}' new - | tac
gla2
fla3
dla4
rfa5
.Ends
shu
sha
she
.Ends
res
pes
ges
.Ends
--->
...
pes
ges
someline
foo
bar
etc
.Ends
# * some irrelevant junk * #
.Ends以上假設您發布的樣本輸入的某些行之后的空白是錯誤的。如果它們真的可以存在,則更改為$0==".Ends",/^\.Ends[[:space:]]*$/或者即使/^[[:space:]]*\.Ends[[:space:]]*$/這些行上也可能有前導空格,或者只是/\.Ends/在之前/之后可以有任何字符.Ends。
uj5u.com熱心網友回復:
輸入:
$ cat test.dat
dla4
.Ends
she
.Ends
res
.Ends
abc
$ cat new.dat
newline 111
newline 222
一種ed方法:
$ ed test.dat >/dev/null 2>&1 <<EOF
1
?.Ends
-1r new.dat
wq
EOF
或者作為單線:
$ ed test.dat < <(printf '%s\n' 1 ?.Ends '-1r new.dat' wq) >/dev/null 2>&1
在哪里:
>/dev/null 2>&1- 強力壓制診斷和資訊訊息1- 轉到第 1 行?.Ends- 在檔案中向后搜索字串.Ends(即.Ends在檔案中查找最后一個)-1r new.dat- 在檔案中向后/向上移動 1 行 (-1) 并r讀取內容new.datwq-w儀式和quit(又名保存并退出)
這會產生:
$ cat test.dat
dla4
.Ends
she
.Ends
res
newline 111
newline 222
.Ends
abc
注意:與將修改后的資料寫入標準輸出的 OP 當前代碼不同,此解決方案修改了原始輸入檔案 ( test.dat)
uj5u.com熱心網友回復:
首先讓我們grep進行搜索,然后用awk.
$ cat insert
new content
new content
$ line=$(cat insert)
$ awk -v var="${line}" '
NR==1{last=$1; next}
FNR==last{print var}1' <(grep -n "^\.Ends$" file | cut -f 1 -d : | tail -1) file
rfa5
.Ends
she
.Ends
ges
.Ends
ges
new content
new content
.Ends
ges
ges
資料
$ cat file
rfa5
.Ends
she
.Ends
ges
.Ends
ges
.Ends
ges
ges
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/537913.html
標籤:perlawksed编辑
