2

我正在尝试在 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);
4

3 回答 3

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,在这种情况下,它不是一个选项(据我所知)。

于 2012-06-19T14:23:51.203 回答
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
于 2012-06-19T14:44:06.787 回答
0

#!行在 Windows 平台上无关紧要,在 Unix 上只是一种方便。最好在这里省略它。

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

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

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

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

还值得指出的是,+>>打开文件以进行读取追加的打开模式很难做到。在这种情况下,您的意思只是>>,但最好打开文件一次,并在程序运行期间保持打开状态。

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

请注意,在File::Find wanted子例程中,$_当前工作目录设置为包含当前报告的文件的目录。$_设置为文件名(没有路径)并$File::Find::name设置为完整的绝对文件和路径。因为当前目录是包含文件的目录,所以打开文件很容易,$_因为不需要路径。

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;
    }
  }
}
于 2012-06-19T14:25:29.573 回答