我有一个正在打开文件的工作Perl脚本,搜索文本字符串。当字符串匹配时,它每次都会打印前面的10行。
我的问题是我怎样才能使其适应目录中的多个文件?
#!/usr/bin/env perl
use strict;
my $file = "myfile.txt";
open (LOGFILE, $file);
my @cont = <LOGFILE>;
close(LOGFILE);
for(my $i = 0; $i <= $#cont; $i++) {
my $line = $cont[$i];
if ($line =~ /Voice VLAN: [0-9]/i) {
my $st;
($i <= 0) ? ($st = 0) : ($st = $i - 10);
my $ln = $i - 1;
my $eln = $i + 1;
my $en = $i + 0;
($en > $#cont) ? ($en = $#cont) : ();
print @cont[$st..$ln];
print $line;
print @cont[$eln..$en];
}
}
答案 0 :(得分:2)
没有必要将整个文件读入内存,保留前一行的缓冲区就足够了
将算法应用于多个文件非常简单:只需打开文件,处理并关闭即可
这是一个模仿grep -A x -B y
的通用解决方案,其中$ B是前面行的计数,$ A是匹配后要打印的以下行数:
<强> grep_AB.pl 强>:
use strict; use warnings;
my $filter=qr/match/;
my ($A,$B)=(1,1);
for my $file(@ARGV) {
open my $fh, '<', $file or die "$file:$!\n";
my (@buffer,$tail);
while(<>) {
if (m{$filter}) {
$tail=1+$A;
print for @buffer;
@buffer=();
}
if ($tail-->0) {
print;
}
else {
push @buffer, $_;
shift @buffer if @buffer>$B;
}
}
close $fh;
}
给出以下输入(input.txt
):
1
2
3
match
match
4
match
5
6
match
7
8
9
10
match
11
12
perl grep_AB.pl input.txt
的输出是:
3
match
match
4
match
5
6
match
7
10
match
11
答案 1 :(得分:1)
如果您可以在命令行上指定文件:
use warnings;
use strict;
my @buf;
while (<>) {
push @buf, $_;
print @buf if /Voice VLAN: [0-9]/i;
shift @buf if @buf>10;
}
如果你想在脚本中指定文件,你可以在&#34; hack it&#34;在循环之前说local @ARGV = ('myfile.txt');
。虽然更干净的解决方案,例如,如果此代码是较长脚本的一部分,则是:
use warnings;
use strict;
my @files = ('myfile.txt');
for my $file (@files) {
open my $fh, '<', $file or die "$file: $!";
my @buf;
while (<$fh>) {
push @buf, $_;
print @buf if /Voice VLAN: [0-9]/i;
shift @buf if @buf>10;
}
close $fh;
}
如果您愿意,也可以在原始代码上使用相同的循环,如评论中提到的@choroba。
更新:如果你想在输出前加上文件名,你可以修改上面第二个例子中的print
,我希望这是相当不言自明的:
if ( /Voice VLAN: [0-9]/i ) {
for my $line (@buf) {
print "$file: $line";
}
}
或者,如果您更喜欢较短的版本,可以将第一个示例中的print
更改为:
print map {"$ARGV: $_"} @buf if /Voice VLAN: [0-9]/i;
做了非常相似的事情。我已使用map
代替for
来循环数组,这意味着print
仅使用字符串列表调用一次。另外,我正在获取&#34; magic&#34;的文件名。 <>
运营商目前正在阅读$ARGV
。