2012-06-19 2 views
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); 
+3

Возможно, вы предпочли бы использовать одну из многих реализаций 'grep' вместо того, чтобы катиться самостоятельно. – bluevector

+0

Как сказано выше, вы можете использовать функцию 'grep()' Perl. Также из опыта я рекомендую вам выводить в STDIN вместо файла (просто 'print()' it). Вы можете перенаправить вывод в файл с помощью '>' redirection. Это позволяет больше гибкости для скрипта (например, переводит вывод на другой процесс и т. Д.). – m0skit0

+0

Я пробовал использовать grep, но он не дал мне результатов, которые я хотел, так как он не показывал мне все содержащиеся подкаталоги. Вот почему я пытаюсь найти другое решение. –

ответ

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, и в этом случае это не вариант (насколько мне известно).

+0

Спасибо, TLP его работа отлично. Затем вы можете объяснить 'if (-f $ element && $ element = ~/\. Txt $ /)' что означает '-f'.? – mkHun

+1

Это описано в ['perldoc -f" -X "'] (http://perldoc.perl.org/functions/-X.html) – TLP

0

Линия #! не имеет отношения к платформам Windows и только удобство для Unix. Лучше всего, если вы опустите его здесь.

Ваша программа в основном правильная, но позволяет избежать множества удобств, которые Perl обеспечивает, чтобы код был более кратким и понятным.

Вы всегда должны добавить use warnings в ваш use strict, так как он подберет простые ошибки, которые вы можете игнорировать.

В открывшемся файле должны использоваться лексические файловые дескрипторы и трехпараметрическая форма open, и вы должны проверить их успех, поскольку отказ открыть файл делает недействительным большинство последующих кодов. Идиоматических открытым выглядит следующим образом

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

Он также worh указывая на то, что открытый режим +>> открывает файл для как читать и Append, который трудно nadle. В этом случае вы имеете в виду только >>, но лучше всего открыть файл один раз и оставить его открытым на время выполнения программы.

Это переработка вашей программы, которая, я надеюсь, поможет вам. Он использует регулярное выражение, чтобы проверить, отображается ли строка в текущей строке файла. /\Q$string/ идентичен $_ =~ /\Q$string/, то есть он проверяет переменную $_ по умолчанию. \Q в регулярном выражении представляет собой quotemeta, который пропускает любые символы в строке, которые в противном случае могли бы вести себя как специальные символы в регулярном выражении и изменять смысл поиска.Обратите внимание, что в пределах File::Findwanted подпрограмма $_ текущая рабочая директория установлена ​​в каталог, в котором сообщается текущий файл. $_ устанавливается в имя файла (без пути), а $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; 
    } 
    } 
} 
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 
Смежные вопросы