我有一个包含此类内容的文件:
bla bla
图案2
bla
图案1
图案2
bla
bla 图案1 bla
bla
图案1
我想删除整行粗体,即包含pattern1
最后一次匹配后第一次出现的pattern2
。
有人有主意吗?
谢谢 !
答案1
这是一条ex
单线。 (ex
是 的前身和脚本形式vi
。)
printf '%s\n' '$?pattern2?/pattern1/d' x | ex file.txt
保存x
并退出。%p
如果您只想打印更改后的文件,请将其更改为不是保存更改(有利于测试)。
$
表示文件的最后一行;是一个地址,表示从当前位置开始?pattern2?
向后搜索的第一个结果;为正向搜索地址,为删除行命令。pattern2
/pattern1/
d
ex
当您需要向前和向后寻址时使用。
vi
您可以在Vim中以交互方式执行相同的操作:
vim file.txt
然后,输入
:$?pattern2?/pattern1/d
并按 Enter 键。
然后保存并按:x
Enter 退出。
答案2
这里有一个暴力方法。读取数据并循环两次。第一次查找最后一次出现的pattern2,第二次查找第一次出现的pattern1。
#!/usr/bin/perl
# usage: perl remove-pattern.pl [file]
use strict;
# reads the contents of the text file completely
# removes end of line character and spurious control-M's
sub load {
my $file = shift;
open my $in, "<", $file or die "unable to open $file : $!";
my @file_contents = <$in>;
foreach ( @file_contents ) {
chomp;
s/\cM//g;
}
return @file_contents;
}
# gets the first file from the command line
# after the perl script
my $ifile = shift;
# read the text file
my @file_contents = &load($ifile);
# set 2 variables for the index into the array
my $p2 = -1;
my $p1 = -1;
# loop through the file contents and find the last
# of pattern2 (could go reverse the data and find the
# first of pattern2
for( my $i = 0;$i < @file_contents; ++$i ) {
if( $file_contents[$i] =~ /pattern2/) {
$p2 = $i
}
}
# start at the location of the last of pattern2
# and find the first of pattern1
for( my $i = $p2; $i < @file_contents; ++$i ) {
if($file_contents[$i] =~ /pattern1/) {
$p1 = $i ;
last;
}
}
# create an output file name
my $ofile = $ifile . ".filtered";
# open the output file for writing
open my $out, ">", $ofile or die "unable to open $ofile : $!";
# loop through the file contents and don't print the index if it matches
# p1. print all others
for( my $i = 0;$i < @file_contents; ++$i ) {
print $out "$file_contents[$i]\n" if ($i != $p1);
}
--- data.txt ---
bla bla
pattern2
bla
pattern1
pattern2
bla
bla pattern1 bla
bla
pattern1
如果上面的 perl 脚本被命名为“remove-pattern.pl”,则在给定 data.txt 输入文件的情况下,将使用以下命令执行该脚本。 %> perl 删除-pattern.pl data.txt
生成的输出文件“data.txt.filtered”
--- data.txt.filtered ---
bla bla
pattern2
bla
pattern1
pattern2
bla
bla
pattern1
答案3
要查找该行的行号:
lineno=$( nl file | tac | awk '/pattern1/ {last = $1} /pattern2/ {print last; exit}' )
用于nl
向文件添加行号、
tac
反转行
以及awk
打印行号最后的“模式1”前这第一的“模式2”。
然后删除该行:
sed -i "${lineno}d" file
答案4
如果您只想在文件中进行一次传递并最大限度地减少内存中保存的行数,则可以使用awk
状态机方法。这些并不是最短的解决方案,但很容易想出和阅读/维护。您可以用数字替换州名称,以使其(可能)更加高效。
PATTERN1=pattern1 PATTERN2=pattern2 awk '
BEGIN {
p1 = ENVIRON["PATTERN1"]
p2 = ENVIRON["PATTERN2"]
state = "init"
}
state == "init" {
if ($0 ~ p2) state = "p2_found"
print
next
}
state == "p2_found" {
if ($0 ~ p1) {
state = "p1_found"
p1_line = $0
printf "%s", hold
hold = ""
} else if ($0 ~ p2) {
# we can print the text held since the last p2
printf "%s", hold
hold = $0 RS
} else hold = hold $0 RS
next
}
state == "p1_found" {
if ($0 ~ p2) {
state = "p2_found"
# the line that matched p1 is not discarded
printf "%s\n%s", p1_line, hold;
hold = ""
}
hold = hold $0 RS
}
END {
# here we are not printing p1_line which is how it is discarded
printf "%s", hold
}'
(我假设没有任何行与pattern1
和匹配pattern2
)。