2

我有一个简单的搜索脚本,它接受用户输入并跨目录和文件进行搜索,并且只列出找到它的文件。我想要做的是能够在找到匹配项时,抓住它上面的 4 行,然后它下面的3行并打印它。所以,可以说我有。

一些文件.html

"a;lskdj a;sdkjfa;klsjdf a aa;ksjd a;kjaf ;;jk;kj asdfjjasdjjfajsd  jdjd
jdjajsdf<blah></blah> ok ok okasdfa stes test tes tes test test<br>

blah blah blah ok, I vouch for the sincerity of my post all day long.
Even though I can sometimes be a little crass.

I would only know the blue moon of pandora if I saw it. I heard tales of long ago 
times in which .. blah blah

<some html>whatever some number 76854</some html>

running thru files of grass etc.. ===> more info
whatever more "

假设我想找到“76854”,它将打印或存储在一个数组中,这样我就可以打印在 dirs/files 中找到的所有匹配项。

*Match found:*

**I would only know the blue moon of pandora if I saw it. I heard tales of long ago 
times in which .. blah blah
<some html>whatever whatever</some html>
running thru files of grass etc.. ===> more info
whatever more**


**********************************

类似的东西。到目前为止,我有并且它正在通过打印出找到匹配项的文件来工作:

if ($args->{'keyword'}){
    if($keyword =~ /^\d+$/){
    print "Your Results are as Follows:\n";
        find( sub
            {
                local $/;
                return if ($_ =~ /^\./);
                return unless ($_ =~ /\.html$/i);
                stat $File::Find::name;
                return if -d; #is the current file a director?
                return unless -r; # is the file readable?
                open(FILE, "< $File::Find::name") or return;
                my $string = <FILE>;
                close (FILE);
                print "$keyword\n";
                if(grep /$keyword/, $string){
                    push(@resultholder, $File::Find::name);
                }else{
                   return;
                }
             },'/app/docs/');
    print "Results: @resultholder\n";
    }else{
        print "\n\n ERROR\n";
        print "*************************************\n\n";
        print "Seems Your Entry was in the wrong format \n\n";
        print "*************************************\n\n";
    }
exit;
}
4

3 回答 3

3

perl 是这里的先决条件吗?使用 grep 非常简单,您可以告诉它在匹配前后打印 N 行。

grep <search-term> file.txt -B <# of lines before> -A <# of lines after>

如果您真的想使用 perl,请忽略,只是扔掉一个替代方案。

于 2012-05-22T22:59:16.520 回答
3

您使用的是 Windows 还是 Linux?

如果您在 Linux 上,您的脚本最好替换为:

grep -r -l 'search_string' path_to_search_directory

它将列出所有包含 search_string 的文件。要在 match 行之前获得 4 行上下文,在行之后获得 3 行上下文,您需要运行:

grep -r -B 4 -A 3 'search_string' path_to_search_directory

如果由于某种原因您不能或不想使用 grep,则需要改进您的脚本。

首先,通过这种结构,您只读取文件中的第一个字符串:

my $string = <FILE>;

其次,您最好避免将所有文件读入内存,因为您可能会遇到几个 Gb 文件。甚至将一个字符串读入内存,因为你会遇到非常大的字符串。将其替换为对一些小缓冲区的顺序读取。

最后,要获得之前的 4 行和之后的 3 行,您需要从找到的匹配中执行反向读取(在匹配之前寻找到 buffer_size 的位置,读取该块并检查其中是否有足够的换行符) .

于 2012-05-22T23:07:37.300 回答
2

因此,您需要存储至少 8 行,并在第 5 行与您的模式匹配时输出这 8 行。shift用于从数组前面删除元素的运算push符和用于将元素添加到列表末尾的运算符在这里可能会有所帮助。

find( sub {
    ...  # but don't set $\

    open( FILE, '<', $File::Find::name) or return;
    my @buffer = () x 8;
    while (<FILE>) {
        shift @buffer;
        push @buffer, $_;
        if ($buffer[4] =~ /\Q$keyword\E/) {
            print "--- Found in $File::Find::name ---\n";
            print @buffer;
            # return?
        }
    }
    close FILE;

    # handle the case where the keyword is in the last ~4 lines of the file.
    while (@buffer > 5) {
        shift @buffer;
        if ($buffer[4] =~ /\Q$keyword\E/) {
            print "--- Found in $File::Find::name ---\n";
            print @buffer;
        }
    }
} );
于 2012-05-22T23:06:25.983 回答