grep segunda vez mais rápido

6

Suponha que eu faça uma pesquisa grep recursiva e demorada. Depois de ver os resultados, quero uma saída diferente; Por exemplo, quero adicionar a opção -C 3 para 3 linhas de contexto. Eu posso fazer toda a pesquisa novamente com a nova opção adicionada, mas eu tenho que esperar o mesmo tempo que antes.

Existe alguma maneira inteligente de fazer com que grep realize a segunda pesquisa mais rapidamente?

    
por student 07.06.2012 / 10:57

5 respostas

1

Você pode salvar a lista de arquivos correspondente e o grep apenas em arquivos correspondentes. Será muito mais rápido. Por exemplo, você pode usar find + grep :

find . -type f -exec grep -l 'PATTERN' {} \+ | xargs grep -H -C 3 'PATTERN'

Se você precisar ver a saída grep após a primeira execução em find , será um pouco mais difícil, mas ainda assim muito fácil. Você só precisa usar algo assim

find -exec grep -H 'PATTERN' {} \+ | tee -a out.log |\
sed 's/^[^:]*://' | sort -u | xargs grep -C 3  'PATTERN'

E a saída será salva no arquivo out.log.

    
por 07.06.2012 / 11:15
7

A segunda vez já deve ser mais rápida (se grep for de E / S), pois o arquivo deve estar no cache do sistema operacional.

Como grep não salva nenhum estado e só funciona com o parâmetro de entrada fornecido, não há como reutilizar os resultados anteriores com grep em si.

Se você tiver esse problema regularmente, talvez queira pesquisar os mecanismos de pesquisa da área de trabalho ou a indexação de texto para melhorar o tempo de pesquisa e os resultados.

    
por 07.06.2012 / 11:02
2

Se os arquivos ainda estiverem no cache de disco, a segunda pesquisa será mais rápida.

Se você quiser acelerar as pesquisas, precisará criar um índice. Isso está bem além do trabalho do grep: é uma ferramenta de busca, não uma ferramenta de indexação. Indexação de texto completo compatível com linhas de comando? lista algumas ferramentas de indexação .

Existem maneiras de aproveitar o grep para fazer buscas repetidas mais rapidamente. Por exemplo, primeiro obtenha a lista de arquivos correspondentes com grep -l . Se os nomes dos seus arquivos não contiverem caracteres curinga de espaço em branco ou *?\[ , você poderá inserir os nomes dos arquivos em uma variável:

f=$(grep -l -r foo .)
grep foo $f
grep -C3 foo $f
grep foobar $f
    
por 08.06.2012 / 02:10
1

Apenas para algo diferente ...
O script a seguir não usa grep na segunda vez. Ele depende apenas dos números de linha reunidos pelo grep no primeiro passo e usa sed para a impressão.

grep -HnZ é usado na primeira etapa: H para o nome do arquivo, n para o número da linha e Z para um delimitador sem texto \x00 entre o nome do arquivo e o linenumber.

Eu não acho que será muito (se houver) mais rápido do que executar grep sobre os arquivos que foram idendificados no primeiro caminho, porque cada um dos arquivos identificados precisa ser verificado em ambos os casos. Também não é preciso se alguma alteração relevante na entrada do conjunto de dados no primeiro passo. (Isso só pegou meu interesse, então aqui está ..)

# create 2 test files.
  printf '%s\n' {a..z} >junk1
  printf '%s\n' {a..z} >junk2

# Make list of filenames and line numbers
# then convert the list into a shell script 
# which uses 'sed' to list the lines
grep -HnZ "[gms]" junk1 junk2 | 
  # Make list of filenames and line numbers
  awk -v"C=2" 'BEGIN{ FS="[\x00:]"
                 print "#!/bin/sh"
               }
               { negC=$2-C; if (negC<1){negC=1}; posC=$2+C }
               prev != $1 { 
                  if( prev ) print prev_grp "\""
                  prev = $1
                  prev_grp = "<\"" $1 "\" sed -nr \"" \
                  negC"i -- ("negC","$2","posC") "$1"\n\t"negC","posC"{p;b};"
                  next 
               }
               {  prev_grp = prev_grp" " \
                  negC"i -- ("negC","$2","posC") "$1"\n\t"negC","posC"{p;b};" 
              }
               END{ if( prev ) print prev_grp "\"" }
              '>junk.sh
chmod +x junk.sh   
./junk.sh

Esta é a saída do comando inicial grep , mostrando o valor nulo como \x00

junk1\x007:g
junk1\x0013:m
junk1\x0019:s
junk2\x007:g
junk2\x0013:m
junk2\x0019:s

Aqui está o script gerado

#!/bin/sh
<"junk1" sed -nr "5i -- (5,7,9) junk1
        5,9{p;b}; 11i -- (11,13,15) junk1
        11,15{p;b}; 17i -- (17,19,21) junk1
        17,21{p;b};"
<"junk2" sed -nr "5i -- (5,7,9) junk2
        5,9{p;b}; 11i -- (11,13,15) junk2
        11,15{p;b}; 17i -- (17,19,21) junk2
        17,21{p;b};"

Aqui está a saída parecida com o grep (n, n, n) que são os filtros de linha (de, correspondidos, para)

-- (5,7,9) junk1
e
f
g
h
i
-- (11,13,15) junk1
k
l
m
n
o
-- (17,19,21) junk1
q
r
s
t
u
-- (5,7,9) junk2
e
f
g
h
i
-- (11,13,15) junk2
k
l
m
n
o
-- (17,19,21) junk2
q
r
s
t
u

Seria bonito simplesmente adicionar cor, mas seria mais fácil usar grep (a menos que isso ofereça algo desejável).

    
por 07.06.2012 / 15:40
1
  1. Você realmente precisa de grep - você usa regexps? fgrep é mais rápido.
  2. GNU grep tem --mmap - de acordo com a página man: «… Em algumas situações, o --mmap produz um melhor desempenho…» (mas também tem alguns problemas, consulte a man page).
  3. Basta salvar o arquivo: números de linha de linhas correspondentes e não voltar a grep - você certamente não precisa fazer isso duas vezes novamente, não é?
por 08.06.2012 / 03:42

Tags