我的文件就像
line 1
line 2
line 3
target
line 5
line 6
line 7
我可以写一个与目标匹配的正则表达式。我需要的是我需要抓住2,3,5,6行。 有没有办法做到这一点?
答案 0 :(得分:6)
如果您不确定使用perl
,则可以使用grep
和上下文线路控制选项轻松提取所需的上下文
grep -A 2 -B 2 target filename | grep -v target
当然target
需要用合适的正则表达式替换。
答案 1 :(得分:4)
#!/usr/bin/perl -w
my $lines = <<EOF
line 1
line 2
line 3
target
line 5
line 6
line 7
EOF
;
# Match a new line, then 2 lines, then target, then 2 lines.
# { $1 } { $3 }
my $re = qr/^.*\n((.*?\n){2})target\n((.*?\n){2}).*$/m;
(my $res = $lines) =~ s/$re/$1$3/;
print $res;
答案 2 :(得分:2)
@lines = ('line 1', 'line 2', 'line 3', 'target', 'line 5', 'line 6', 'line 7');
my %answer;
$regex = 'target';
for my $idx (0..$#lines) {
if ($lines[$idx] =~ /$regex/) {
for $ii (($idx - 2)..($idx + 2)){
unless ($lines[$ii] =~ /^$regex$/) {$answer{$ii} = $lines[$ii];}
}
}
}
foreach $key (sort keys %answer) { print "$answer{$key}\n" }
哪个收益......
[mpenning@Bucksnort ~]$ perl search.pl
line 2
line 3
line 5
line 6
[mpenning@Bucksnort ~]$
修复了@ leonbloy关于文件中多个目标字符串的评论
答案 3 :(得分:2)
将文件粘贴到列表/数组中,找到匹配行的索引,并使用此索引获取所需的值(使用偏移量)
答案 4 :(得分:2)
尽管8个月前就提到了这个问题,但我不得不重新考虑这个问题,因为找不到任何解决方案都符合我的目标。我的目标是制作一个脚本,检查许多巨大的日志文件,并从中提取,只包含想要的行,在包含搜索模式的行之前和之后放置可选行数,没有任何冗余。我试图重用这里找到的一些代码,但对我来说这些代码都不够好。所以最后我创造了一个独特的,可能不是最美丽的,但看起来很有用,所以我想与你分享:
use strict;
my @findwhat = ('x');
my $extraLines = 3;
my @cache = ('') x ($extraLines);
my @stack;
my $lncntr = 0;
my $hit = 0;
my $nextHitWatch = 0;
my $shift = 1;
open (IN, "<test1.log");
while (my $line=<IN>) {
$lncntr++;
chomp $line;
foreach my $what (@findwhat) {if ($line =~ m/$what/i) {$hit = 1; last}}
if ($hit && !$nextHitWatch) {
@stack = @cache;
$hit = 0;
$nextHitWatch++;
}
if (!$hit && $nextHitWatch && $nextHitWatch < ($extraLines * 2) + 2) {
@stack = (@stack, $line);
$nextHitWatch++;
}
if (!$hit && $nextHitWatch && $nextHitWatch == ($extraLines * 2) + 2) {
@stack = (@stack, $line);
for (my $i = 0; $i <= ($#stack - ($extraLines + $shift)); $i++) {
print $stack[$i]. "\n" if $stack[$i];
}
$nextHitWatch = 0;
$shift = 1;
@stack = ();
}
if ($nextHitWatch >= 1 && eof) {
foreach(@stack) {print "$_\n"}
}
if ($nextHitWatch >= 1 && eof) {
if (!$hit) {
my $upValue = 3 + $#stack - ($nextHitWatch - $extraLines + $shift);
$upValue = ($upValue > $#stack) ? $#stack : $upValue;
for (my $i = 0; $i <= $upValue; $i++) {
print $stack[$i] . "\n";
}
} else {
foreach (@stack) {print "$_\n"}
}
}
shift(@cache);
push(@cache, $line);
}
close (IN);
可能您只需要更改列表@findwhat和标量$ extraLines的值。我希望我的代码可用。 (抱歉我的英语不好)
答案 5 :(得分:0)
多行正则表达式,例如:/\n{3}(foo)\n{3}/m;
修改强>
/\n*(foo)\n*/m
适用于一般情况
答案 6 :(得分:0)
一个班轮版本(其中-l
= chomp
和-n
= while(<>){}
。有关更多选项,请参阅perldoc
perlrun
:
perl -lnE '$h{$.}=$_; END {
for ( grep { $h{$_} eq "target" } sort{ $a <=> $b } keys %h ) {
say for @h{$_-2..$_-1 , $_+1..$_+2} } }' data.txt
带解释的脚本:
#!perl
use feature 'say';
while (<DATA>) {
chomp;
$hash{$.} = $_ ; # hash entry with line number as key; line contents as value
}
# find the target in the hash and sort keys or line numbers into an array
@matches = sort {$a <=> $b} grep { $hash{$_} eq 'target' } keys %hash;
for (@matches) {
say "before\n" ;
say for @hash{$_-2..$_-1} ; # print the context lines as a hash slice
say ">>>>\" $hash{$.} \"<<<< " ;
say "after\n" ;
say for @hash{$_+1..$_+2} ;
say "";
}
__DATA__
line 1
line 2
line 3
target
line 5
line 6
line 7
target
line of context1
line of context2
target
<强>输出强>:
before
line 2
line 3
>>>>" target "<<<<
after
line 5
line 6
before
line 6
line 7
>>>>" target "<<<<
after
line of context1
line of context2
before
line of context1
line of context2
>>>>" target "<<<<
after
一个更简单的版本,仅使用数组和输出,在请求的OP问题时排除目标:
#!perl -l
chomp( my @lines = <DATA> ) ;
my $n = 2 ; # context range before/after
my @indexes = grep { $lines[$_] =~ m/target/ } 0..$#lines ;
foreach my $i (@indexes) {
print for @lines[$i-$n..$i-1], @lines[$i+1..$i+$n],"";
}
__DATA__
line 1
line 2
line 3
target
line 5
line 6
line 7
target
line of context1
line of context2
target
这可以避免构造散列,但在非常大的文件/数组上可能会更慢。
在CPAN List::MoreUtils
上有indexes()
且始终有splice()
,但我不确定这会让事情变得更简单。