Pesquisar diretórios para string de várias linhas

5

Procurando uma maneira de pesquisar recursivamente um repositório para todos os arquivos que contenham uma cadeia de várias linhas e retornar os nomes de arquivo que o contêm. O parágrafo é apenas um cabeçalho de aproximadamente 30 linhas. Então eu sei que pcregrep pode fazer uma pesquisa em várias linhas. Mas você precisa colocar toda a expressão. Como este é um longo parágrafo que eu estou procurando, eu quero colocá-lo na variável e apenas fazer um grep dessa variável. Então, basicamente, seria find . -name "*.[ch]" | xargs pcregrep -M $paragraph Mas isso não funciona, alguém poderia me apontar na direção certa.

    
por Gooner1990 04.02.2016 / 13:20

2 respostas

2

Como você está tentando corresponder as primeiras 30 linhas de seus arquivos, você pode salvar o texto em um arquivo, por exemplo: ref_file , em seguida, use diff para comparar o arquivo de referência com as primeiras 30 linhas de cada arquivo:

find . -name "*.[ch]" -exec ./myscript {} \; -print

em que ./myscript é

#!/bin/sh

head -n 30 "$1" | diff - /path/to/ref_file >/dev/null

so -print no primeiro comando só é executado se o -exec anterior avaliou verdadeiro , ou seja, se não houve diferença entre o arquivo de referência e as primeiras 30 linhas do arquivo atual

Ou, se você preferir sem um script e salvando os nomes dos arquivos em logfile :

find . -type f -exec sh -c 'head -n 30 "$0" | diff - /path/to/ref_file >/dev/null' {} \; -print >logfile

Observe que isso pressupõe que você esteja procurando uma correspondência exata. Caso contrário, diff sairá com 1 , mesmo que a diferença seja um único espaço.

    
por 04.02.2016 / 16:19
2

Você pode encontrar cada arquivo a ser processado usando find e alimentar cada nome de arquivo em um script feito de propósito para procurar uma correspondência e imprimir o nome do arquivo no caso de uma correspondência; Eu sugiro usar um script em vez de uma linha única para a facilidade adicional de lidar com a cadeia de várias linhas em comparação com o prompt.

Isto é:

find . -name "*.[ch]" -exec /path/to/script {} \;

Em que script é este script Perl:

#! /usr/bin/perl

$/ = ""; # sets the input record separator to an empty string
$_ = <>; # stores the content of the file specified in the first argument in $_

$string = <<EOF; # The multi-line string to match starts here
My
multiline
string
EOF
# The multi-line string to match ends here

/\Q$string\E/ && print($ARGV."\n"); # If $_ matches $string, prints the name of the file
  • $/ = ""; : define o separador de registro de entrada do Perl como uma string vazia; isso tem o efeito de fazer com que o Perl leia o arquivo inteiro especificado no primeiro argumento para o script de uma só vez;
  • $_ = <>; : armazena o conteúdo do arquivo especificado no primeiro argumento em $_ ;
  • $string = <<EOF; [...] EOF : armazena o conteúdo de [...] $string (substitua [...] pela sequência de várias linhas para corresponder);
  • /\Q$string\E/ && print($ARGV."\n"); : se $_ corresponder a $string , imprime o nome do arquivo.

Esta é a saída de exemplo em uma hierarquia de diretório de teste feita de propósito:

% for f in *; do printf '%s:\n\n' "$f"; <<<'' cat "$f" -; done
file1:

My
multiline
string

file2:

My
multiline
string

file3:

My
other
multiline
string

script.pl:

#! /usr/bin/perl

$/ = "";
$_ = <>;

$string = <<EOF;
My
multiline
string
EOF

$string = quotemeta($string);

/$string/&&print($ARGV."\n");

% find . -type f -exec ./script.pl {} \;                      
./file2
./file1
    
por 04.02.2016 / 14:11