Grep um diretório e lista de retorno com números de linha

4

No momento, estou tentando aprender mais sobre scripts de bash e todas essas coisas divertidas e compilei este pequeno comando:

find $path | xargs grep -n $pattern | awk '{print $1}'

Enquanto isso funciona, eu queria saber se eu estava reinventando a roda. Existe uma maneira melhor de pesquisar em um diretório, fazer uma busca nos arquivos por um padrão e retornar uma lista com números de linha?

Desculpe antecipadamente se este for um lugar inadequado / fora do tópico para postar esta pergunta.

    
por Zack Hovatter 29.11.2011 / 21:49

6 respostas

6

Muitas grep variants implementam uma opção recursiva. Por exemplo, GNU grep

-R, -r, --recursive
          Read all files under each directory, recursively; this is equivalent to the -d recurse option.

Você pode então remover find :

grep -n -r $pattern $path | awk '{ print $1 }'

mas isso mantém mais do que o número da linha. awk está imprimindo a primeira coluna. Este exemplo

src/main/package/A.java:3:import java.util.Map;
src/main/package/A.java:5:import javax.security.auth.Subject;
src/main/package/A.java:6:import javax.security.auth.callback.CallbackHandler;

será impresso como

src/main/package/A.java:3:import
src/main/package/A.java:5:import
src/main/package/A.java:6:import

observe o :import em cada linha. Você pode querer usar sed para filtrar a saída.

Como um : pode estar presente no nome do arquivo, você pode usar a opção -Z do grep para gerar um caractere nulo (\ 0) após o nome do arquivo.

grep -rZn $pattern $path | sed -e "s/[[:cntrl:]]\([0-9][0-9]*\).*/:/" 

com o mesmo exemplo de antes produzirá

src/main/package/A.java:3
src/main/package/A.java:5
src/main/package/A.java:6
    
por 29.11.2011 / 22:05
3

Para a primeira parte, observe que xargs só funciona se não houver caracteres em branco ou \'" em seus nomes de arquivos. Veja Como para procurar uma palavra em todo o conteúdo de um diretório no linux para uma explicação e uma alternativa.

Além disso, sempre coloque aspas duplas em torno das substituições de variáveis: "$path" . Sem as aspas duplas, o shell expande os espaços em branco e os caracteres curinga no valor de $path , portanto, usa as quebras não indicadas se você tiver espaços em branco ou caracteres curinga nesse nome de arquivo. O mesmo vale para $pattern (apenas para risos, tente deixar as citações e procure por h* em um diretório contendo arquivos chamados hi e hello ).

Se a sua versão de grep tiver a opção -r para percorrer diretórios recursivamente, você não precisará de find aqui. A opção -r está presente no Linux, FreeBSD, Mac OS X e Cygwin, entre outros. Caso contrário:

find "$path" -type f -exec grep -Hn "$pattern" {} + | awk -F: '{print $1 ":" $2}'

Também consertei sua chamada awk acima, para que ela imprima apenas o nome do arquivo e os números de linha. Eu também passo a opção -H para grep , para garantir que ele sempre imprime o nome do arquivo, mesmo que exista um único arquivo. Esse código pressupõe que os nomes dos arquivos não contenham : ou newlines; se puderem, as coisas ficam complicadas, e é melhor que você dependa da opção -Z do GNU grep ou processe os arquivos individualmente:

find "$path" -type f -exec sh -c 'for x; do grep -n "$0" <"$x" | awk -v fn="$x" -F: 'print fn ":" $1'; done' "$pattern" {} +
    
por 30.11.2011 / 02:36
1

Eu me livraria do grep e usaria awk :

find $path -type f -print0 | xargs -0 awk "/$pattern/{print FILENAME,FNR}"

Mas usando grep e cut :

find $path -type f -print0 | xargs -0 grep -nH "$pattern" | cut -d: -f1,2

Inclua a cláusula -type f para que você não obtenha erros tentando pesquisar (no grep ou awk) em tipos de arquivo não regulares (links simbólicos, diretórios, soquetes). Se você ler um pipe ou um soquete quando outro programa deveria estar, então você pode atrapalhar o programa.

O find ... -print0 | xargs -0 fica com espaços em branco nos nomes dos arquivos. Não está disponível em todos os sistemas UNIX, mas está no máximo.

    
por 30.11.2011 / 03:37
0

Não tem certeza do que exatamente você está tentando fazer aqui.

find $path | xargs grep -n $pattern | awk '{print $1}'

Para mim, isso se traduz em encontrar todos os arquivos em $ path e pesquisá-los com linhas numeradas para pattern $ pattern e imprimir o número da linha e a primeira palavra da linha que corresponde a $ pattern. (possivelmente não incluindo $ padrão em si)

Se for esse o caso, então você está reinventando um pouco a roda. Você pode fazer tudo isso diretamente do comando find, sem a penalidade extra do pipe xargs.

find $path -exec grep -n $pattern {} \; -print | awk '{print $1}'

ou remova o cachimbo awk para todo o conteúdo da linha.

Usar -exec do próprio usuário tem o benefício adicional de manipular espaços em branco em nomes de arquivos.

    
por 30.11.2011 / 03:04
0

verifique também as opções -c e -n .

    
por 30.11.2011 / 10:10
-1

Veja o que eu faria:

  • evite usar tantos canos. Sempre que possível, use uma solução alternativa. Em vez de find . | grep -n <> , por que não usar -exec ?

    • Você também pode aproveitar a substituição do processo.

Tente fazer o seguinte:

awk '{print $1}' <(find $path -exec grep -n $pattern {} \;)

NB: Isso pode funcionar como está, ou com uma pequena variação, dependendo do shell e da versão do find que você está usando.

    
por 29.11.2011 / 22:09