Sua pergunta e meu entendimento
Sua pergunta atualmente carece de exemplos concretos de entrada e saída desejada, por isso vou tentar responder a sua resposta como eu a entendo , e editar de acordo quando você fornecer mais informações.
A maneira como eu entendi sua pergunta agora é que você está executando algo nas seguintes linhas:
find /path/to/directory -exec grep -H -n 'SomeString' {} \;
Que produz um resultado que é algo assim:
$ find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \;
/home/serg/fortesting/file3:1:HelloWorld
/home/serg/fortesting/file1:4:HelloWorld
Ou, em geral, /path/to/file:lineNumber:String
Soluções possíveis
Apropriadamente, este é um trabalho para awk
: você tem 3 campos separados por dois pontos (separador de campo), o que se traduz em código awk awk -F":" '{printf $1 FS $2 FS "\n" $3 "\n" }'
Assim, podemos fazer o seguinte:
$ find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \; | awk -F ":" '{printf $1 FS $2 FS "\n" $3 "\n" }'
/home/xieerqi/fortesting/file3:1:
HelloWorld
/home/xieerqi/fortesting/file1:4:
HelloWorld
Agora, awk
é uma ferramenta versátil; podemos imitar a saída de find -exec grep
com 'find -exec awk' (código awk aqui) ', que já será processado e será salva na tubulação.
Considere o código abaixo:
$ find $PWD -type f -exec awk '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} \;
/home/xieerqi/fortesting/file3:1
HelloWorld
/home/xieerqi/fortesting/file1:4
HelloWorld
Menos tubulação e o conteúdo estão sendo processados à medida que são encontrados. Além disso, se o arquivo tiver dois pontos em seu nome, esse código ainda processará corretamente, pois não estamos dependendo dos separadores de campo, mas sim da variável FILENAME seguido por dois pontos, seguido por FNR (o número de registro de entrada no arquivo de entrada atual) e a linha encontrada separada por nova linha.
Eficiência
Agora, vamos considerar a eficiência conforme o número de arquivos é grande.
Primeiro, crio os arquivos file1
para file1000
e, em seguida, usamos /usr/bin/time
para testar cada versão do comando.
$ echo 'HelloWorld' | tee file{$(seq -s',' 1 1000)}
$ /usr/bin/time find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \; | awk -F ":" '{printf $1 FS $2 FS "\n" $3 "\n" }' > /dev/null
0.04user 0.34system 0:03.09elapsed 12%CPU (0avgtext+0avgdata 2420maxresident)k
0inputs+0outputs (0major+113358minor)pagefaults 0swaps
$ /usr/bin/time find $PWD -type f -exec awk '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} \; > /dev/null
0.82user 2.03system 0:04.25elapsed 67%CPU (0avgtext+0avgdata 2856maxresident)k
0inputs+0outputs (0major+145292minor)pagefaults 0swaps
Assim, a versão longa parece ser mais eficiente, leva menos tempo e porcentagem de CPU.
Agora, aqui está um compromisso - altere \;
para +
:
/usr/bin/time find $PWD -type f -exec awk '/HelloWorld/ {print FILENAME":"NR"\n"$0 }' {} +
O que faz o operador +
? A grande diferença é que +
informa ao exec para listar tantos arquivos quanto a entrada para o comando awk
quanto possível, enquanto \;
faz com que awk
seja chamado toda vez para cada arquivo encontrado.
$ /usr/bin/time find $PWD -type f -exec awk '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} + > /dev/null
0.00user 0.02system 0:00.02elapsed 74%CPU (0avgtext+0avgdata 3036maxresident)k
0inputs+0outputs (0major+398minor)pagefaults 0swaps
Hey, muito mais rápido, certo? Embora ainda pesado na CPU.
Saída para outro arquivo
Quanto à saída para outro arquivo, adicione o operador >
para redirecionamento