A impressão do nome do arquivo junto com o grep resulta em find -exec

2

Eu tenho um programa chamado foo , que eu quero executar em cada resultado que é encontrado via find. Então, algo assim:

find . -name '*.o' -type f -exec foo {} \;

Eu quero grep a saída de todas essas invocações de foo para uma determinada barra de strings. Então eu adiciono isso:

find . -name '*.o' -type f -exec foo {} \; | grep bar

Mas eu perco as informações originais sobre os arquivos dos quais as correspondências são provenientes. Eu tentei adicionar -fprintf /dev/stderr '%p\n' ao comando find , mas agora parece que stdout sumiu, porque nenhum resultado grep é impresso.

Como posso obter cada nome de arquivo impresso na saída e ENTÃO ter os resultados do grep correspondentes ao arquivo impresso depois?

Como alternativa, se houvesse alguma maneira de tornar o argumento -H de grep work, isso seria ótimo também, mas, como foi escrito, não funcionará porque estou passando o texto de stdin e grep não sabe o nome do arquivo. Eu tentei vários encantamentos de xargs , mas também não consegui fazer nada disso funcionar.

    
por Zachary Turner 15.12.2017 / 20:14

3 respostas

2

Para obter cada nome de arquivo impresso na saída e, ENTÃO, ter os resultados do grep correspondentes àquele arquivo impresso depois, você pode agrupar o -exec foo {} | grep pipe em um shell:

find . -name '*.o' -type f -print -exec sh -c 'foo "$1" | grep "bar"' sh {} \;

Para fazer com que o argumento -H do grep funcione com a entrada padrão, se a sua versão de grep suportar a opção --label= , você poderia fazer

find . -name '*.o' -type f -exec sh -c '
  foo "$1" | grep -H --label="$1" "bar"
' sh {} \;

ou (se a sua localização suportar a alternativa + de vários argumentos para \; ):

find . -name '*.o' -type f -exec sh -c '
  for f; do foo "$f" | grep -H --label="$f" "bar"; done
' sh {} +
    
por 15.12.2017 / 21:27
1

Isso é muito feio, mas funcionará para você:

find . -name '*.o' -type f -exec sh -c 'foo "$1" | sed "s@^@$1\t@"' _ {} \;

Isso colocará o nome do arquivo e uma guia no início de cada linha. O sublinhado vazio é uma string que preenche $0 no shell gerado, deixando o nome do arquivo como $1 . Agora você pode grep a saída do find, e o nome do arquivo aparecerá na saída.

Isso quebrará qualquer nome de arquivo contendo o caractere @ . Se isso é uma preocupação, podemos aumentar o script de shell:

find . -name '*.o' -type f -exec sh -c '
    r=$(echo "$1" | sed "s/@/\\\\@/g")
    foo "$1" | sed "s@^@$r\t@"
' _ {} \;

8 barras invertidas foram determinadas por tentativa e erro ...

Ou usando o bash (ou ksh) que tem uma conveniente substituição de parâmetro "pesquisar e substituir"

find . -name '*.o' -type f -exec ksh -c 'foo "$1" | sed "s@^@${1//@/\\@}\t@"' _ {} \;
    
por 15.12.2017 / 20:54
0

How can I get each filename printed to the output and THEN have the grep results corresponding to that file printed after?

1.) use duas instâncias do grep, uma para imprimir o nome do arquivo e outra para imprimir a correspondência:

find . -name '*.o' -type f -exec grep -l bar {} \; -exec grep bar {} \;

2.) faça o grep se comportar como se fosse vários arquivos:

find . -name '*.o' -type f -exec grep bar {} /dev/null \;

isto não resolve o problema com [..]-exec foo {} \;[...] e executando o grep dentro de um pipe como no seu exemplo.

    
por 15.12.2017 / 21:02