读取搜索字符串的文件并打印其路径

时间:2012-06-19 14:02:42

标签: string perl file search

我正在尝试在Perl中编写一个脚本,用于搜索特定目录和所有子目录。这样做的目的是脚本必须读取目录中的所有文件以及查找特定文本字符串(我定义的任何字符串)的所有子目录。如果在文件中找到该字符串,则脚本将在新文本文件中打印文件的路径和名称,并继续目录树中的所有文件。

我有这样的感觉,但我不知道如何继续。我是Perl的初学者,并且不知道所有选项。

#!/usr/bin/perl
use strict;
use File::Find;

my $dir = 'C:\PATH\TO\DIR';
my $string = "defined";

find(\&printFile, $dir);
sub printFile {
   my $element = $_;
   open FILE, "+>>Results.txt";
   if(-f $elemento && $elemento =~ /\.txt$/) {
       my $boolean = 0;
       open CFILE, $elemento;
       while(<CFILE>) {  
           if ($string) {
               print FILE "$File::Find::name\n"; 
           }
           close CFILE;
      }
   }
   close FILE;
}

sleep(5);

3 个答案:

答案 0 :(得分:2)

你并不太遥远,但有些事情你需要改变。

#!/usr/bin/perl
use strict;
use warnings;  # never go without warnings
use File::Find;

my $dir = 'C:\PATH\TO\DIR';
my $string = "defined";
open my $out, ">>", "Results.txt" or die $!;  # move outside, change mode, 
                                              # 3-arg open, check return value
find(\&printFile, $dir);

sub printFile {
   my $element = $_;
   if(-f $element && $element =~ /\.txt$/) { # $elemento doesn't exist
       open my $in, "<", $element or die $!;
       while(<$in>) {
           if (/\Q$string\E/) {  # make a regex and quote metachars 
               print $out "$File::Find::name\n"; 
               last;             # stop searching once found
           }
      }
   }  # lexical file handles auto close when they go out of scope
}

更好的方法是放弃硬编码值并跳过特定的输出文件:

my $dir = shift;
my $string = shift;

然后将输出打印到STDOUT。

print "$File::Find::name\n"; 

用法:

perl script.pl c:/path/to/dir > output.txt

正如其他人在评论中指出的那样,这很容易通过递归grep来解决。但不幸的是你似乎在使用Windows,在这种情况下它不是一个选项(据我所知)。

答案 1 :(得分:1)

如果这真的是你需要做的全部,你可以看一下ack。它将默认搜索子目录,以及grep上的其他增强功能。当然,如果这是一个更大的Perl脚本,那么你可以使用它来发布它,或者使用其他一个发布的答案。

$ ack include

将返回类似

的内容
src/draw.c
27:#include <stdio.h>
28:#include <stdlib.h>
29:#include "parsedef.h"
31:#include "utils.h"
32:#include "frac.h"
33:#include "sscript.h"

src/utils.c
27:#include <stdio.h>
28:#include <stdlib.h>
29:#include <string.h>

......等等

如果相反,您只希望匹配文件的名称使用-l标志

$ ack -l include

lib/Text/AsciiTeX.xs
src/limit.c
src/sscript.c
src/dim.c
src/frac.c
src/brace.c
src/symbols.c
src/sqrt.c
src/array.c
src/ouline.c
src/draw.c
src/utils.c
src/asciiTeX.c

答案 2 :(得分:0)

#!行与Windows平台无关,只在Unix上方便。最好省略它。

您的程序大部分都是正确的,但避免了Perl为使代码更简洁和易于理解而提供的许多便利。

您应始终将use warnings添加到use strict,因为它会发现您可能会忽略的简单错误。

您打开的文件应该使用词法文件句柄和open的三参数形式,您应该检查它们的成功,因为打开文件失败会使大多数后续代码无效。惯用的开放看起来像这样

open my $fh, '<', 'myfile' or die $!;

还有人指出,+>>的开放模式会打开两个读取追加的文件,这很难解决。在这种情况下,您只需要>>,但最好打开文件一次,并在程序运行期间保持打开状态。

这是您的计划的改造,我希望对您有所帮助。它使用正则表达式来检查字符串是否出现在文件的当前行中。 /\Q$string/$_ =~ /\Q$string/相同,即默认情况下会测试$_变量。正则表达式中的\Qquotemeta,它会转义字符串中可能在正则表达式中表现为特殊字符的任何字符,并更改搜索的含义。

请注意,在File::Find wanted子例程$_内,当前工作目录设置为包含当前报告文件的目录。 $_设置为文件名(没有路径),$File::Find::name设置为完整的absolue文件和路径。因为当前目录是包含该文件的目录,所以只需打开文件$_就可以了,因为不需要路径。

use strict;
use warnings;

use File::Find;

my $dir = 'C:\path\to\dir';
my $string = 'defined';

open my $results, '>', 'results.txt' or die "Unable to open results file: $!";

find (\&printFile, $dir);

sub printFile {

  return unless -f and /\.txt$/;

  open my $fh, '<', , $_ or do {
    warn qq(Unable to open "$File::Find::name" for reading: $!);
    return;
  };

  while ($fh) {
    if (/\Q$string/) {
       print $results "$File::Find::name\n";
       return;
    }
  }
}