Para um diretório grande, crie uma variável dos nomes de arquivos que incluam linhas que incluam a string de texto armazenada em outra variável

1

Eu tenho um diretório com mais de 150 mil arquivos. Eu quero criar uma lista de todos os arquivos que contêm o texto armazenado em uma variável de texto, armazenando essa lista de arquivos em outra variável.

Eu tentei pela primeira vez:

searchtext="Subject: Your"
files = $(grep "$searchtext" ./* | awk '{print ($1)}' )

, e enquanto isso funciona para um número moderado de arquivos no diretório, ele gera um erro "Argument list too long" quando executado no diretório com 150K arquivos. (O awk with print foi usado para extrair apenas o nome do arquivo do resultado do grep).

Descobri que arquivos = $ (grep "$ searchtext" ./* | awk '{print ($ 1)}') funciona para o diretório de arquivos de 150K, mas leva quase 90 minutos para ser executado.

Se presente no arquivo, a string $ searchtext será localizada no começo do arquivo. Então eu pensei que poderia acelerar muito se o grep estivesse restrito a dizer as primeiras 30 linhas de texto. Não tendo certeza de como isso poderia ser feito, achei link e tentei várias das sugestões lá. O que parecia mais adequado para minha tarefa era:

searchtext="Subject: Your"
find . -type f -exec head -n 30 {} + | grep "$searchtext"

Isso é executado em um horário aceitável, mas não exibe os nomes de arquivo dos arquivos que contêm o texto da pesquisa. Eu trredi grep -l, mas isso resulta em um erro: "find: head' terminated by signal 13 ". Somewhere it was suggested that the using "\" instead of "+" might be more appropriate. However, that also generates an error: "find: missing argument to -exec '".

Olhando para o futuro quando o resultado do grep inclui os nomes dos arquivos, espero outro problema. Quando tento atribuir a saída do grep a uma variável como:

files = $(find . -type f -exec head -n 30 {} + | grep "$searchtext")

Eu recebo um erro "ut1.sh: linha 16: arquivos: comando não encontrado". Por algum motivo, a variável "arquivos" está sendo interpretada como um comando? Meu nome de script é ut1.sh. Eu atribui variáveis dessa forma muitas vezes antes sem problema.

Minha versão bash é GNU bash, versão 4.1.2 (2) -release (x86_64-redhat-linux-gnu)

Como fazer o trabalho e o que havia de errado com minhas tentativas?

obrigado

    
por Mike 04.07.2017 / 21:28

1 resposta

1

Para obter a lista de nomes de arquivos que grep corresponde, você pode usar a opção -l para obter apenas o nome do arquivo, não é necessário usar awk para processar a saída. Isso também é mais rápido no caso de arquivos correspondentes, pois grep pode parar depois que o padrão é encontrado uma vez.

grep -le "$searchtext" ./* 

Você pode colocar a saída disso em uma variável, com atribuição simples (mas nomes de arquivos com espaços em branco e caracteres glob causam problemas):

files=$(grep -le "$searchtext" ./* ) 

Quanto a isso:

find . -type f -exec head -n 30 {} + | grep "$searchtext"

O canal aqui separa o find e o grep , portanto, você está efetivamente concatenando as primeiras 30 linhas de cada arquivo (perdendo o controle dos nomes dos arquivos aqui) e, em seguida, aplicando o resultado. grep -l só pode dizer se existem correspondências em toda a entrada. Você precisaria executar um shell a partir de find para combinar os head e grep para cada arquivo individualmente:

export searchtext
find . -type f -exec sh -c 'head -n 30 "$1" | grep -q "$searchtext" && echo "$1"' sh {} \;

Mas também podemos usar awk para fazer isso. Isso procuraria o padrão apenas nas primeiras 30 linhas (GNU awk):

awk -vpattern="$searchtext" 'FNR <= 30 && $0 ~ pattern { print FILENAME; nextfile }' *

ou com o find:

find . -type f -exec awk -vpattern="$searchtext" 'FNR <= 4 && $0 ~ pattern { print FILENAME; nextfile }' {} +
    
por 04.07.2017 / 21:48

Tags