Por que o comando “find | grep 'filename' ”muito mais lento que“ find 'filename' ”?


10

Eu tentei os dois comandos e o comando find | grep 'filename' é muitas vezes mais lento que o find 'filename' comando simples .

Qual seria uma explicação adequada para esse comportamento?


2
Você está listando todos os arquivos com find e, em seguida, passa os dados para o grep processar. Com o find usado por si só, você está perdendo a etapa de passar todos os arquivos listados para o grep para analisar a saída. Portanto, isso será mais rápido.
Raman Sailopal 3/10

Mais devagar em que sentido? Os comandos levam um tempo diferente para serem concluídos?
Kusalananda

1
Não consigo reproduzir isso localmente. Se alguma coisa, time find "$HOME" -name '.profile'relata mais tempo que time find "$HOME" | grep -F '.profile'. (17s vs. 12s).
Kusalananda

2
@JenniferAnderson Corri ambos repetidamente. Os 17 e 12 segundos são médias. E sim, a grepvariação corresponderá a qualquer lugar no findresultado, enquanto a correspondência com find -namesomente corresponderá exatamente (neste caso).
Kusalananda

2
Sim, find filename seria rápido . Eu meio que assumi que isso era um erro de digitação e que o OP significava find -name filename. Com find filename, apenas filenameseria examinado (e nada mais).
Kusalananda

Respostas:


11

(Estou assumindo o GNU findaqui)

Usando apenas

find filename

seria rápido, porque retornaria apenas filename, ou os nomes dentro, filenamese for um diretório, ou um erro se esse nome não existir no diretório atual. É uma operação muito rápida, semelhante a ls filename(mas recursiva se filenamefor um diretório).

Em contraste,

find | grep filename

permitiria findgerar uma lista de todos os nomes do diretório atual e abaixo, que grepseriam filtrados. Obviamente, isso seria uma operação muito mais lenta.

Estou assumindo que o que realmente era pretendido era

find . -type f -name 'filename'

Seria filenameo nome de um arquivo comum em qualquer lugar do diretório atual ou abaixo.

Isso será tão rápido (ou comparativamente rápido) quanto find | grep filename, mas a grepsolução corresponderia filenameao caminho completo de cada nome encontrado, da mesma forma que -path '*filename*'faria com find.


A confusão vem de um mal-entendido de como findfunciona.

O utilitário usa vários caminhos e retorna todos os nomes abaixo desses caminhos.

Em seguida, você pode restringir os nomes retornados usando vários testes que podem atuar no nome do arquivo, no caminho, no registro de data e hora, no tamanho do arquivo, no tipo de arquivo etc.

Quando voce diz

find a b c

você perguntar findpara listar todos os nomes disponíveis sob as três caminhos a, be c. Se esses forem nomes de arquivos regulares no diretório atual, eles serão retornados. Se algum deles for o nome de um diretório, ele será retornado juntamente com todos os outros nomes dentro desse diretório.

Quando eu faço

find . -type f -name 'filename'

Isso gera uma lista de todos os nomes no diretório atual ( .) e abaixo. Em seguida, restringe os nomes aos arquivos comuns, ou seja, não diretórios etc., com -type f. Depois, há uma restrição adicional aos nomes que correspondem ao filenameuso -name 'filename'. A string filenamepode ser um padrão globbing de nome de arquivo, como *.txt(lembre-se de citá-la!).

Exemplo:

O seguinte parece "encontrar" o arquivo chamado .profileno meu diretório pessoal:

$ pwd
/home/kk
$ find .profile
.profile

Mas, na verdade, apenas retorna todos os nomes no caminho .profile(existe apenas um nome e esse é o arquivo).

Então cdsubo um nível e tente novamente:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

O findcomando agora não pode encontrar nenhum caminho chamado .profile.

No entanto, se eu procurar o diretório atual e restringir apenas os nomes retornados.profile , ele também será encontrado a partir daí:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenamesó iria voltar filenamese filenamenão era do tipo diretório (ou era do tipo diretório, mas não tem qualquer entrada própria)
Stéphane Chazelas

2

Explicação não técnica: Procurar por Jack na multidão é mais rápido do que procurar todos na multidão e eliminar tudo da consideração, exceto Jack.


O problema é que o OP espera que Jack seja a única pessoa na multidão. Se for, eles têm sorte. find jacklistará jackse é um arquivo chamado jackou todos os nomes no diretório, se for um diretório. É um mal-entendido de como findfunciona.
Kusalananda

1

Ainda não entendi o problema, mas posso fornecer mais algumas idéias.

Como para Kusalananda, a find | grepligação é claramente mais rápida no meu sistema, o que não faz muito sentido. No começo, assumi algum tipo de problema de buffer; essa gravação no console diminui o tempo para o próximo syscall para ler o próximo nome de arquivo. A gravação em um pipe é muito rápida: cerca de 40MiB / s, mesmo para gravações de 32 bytes (no meu sistema bastante lento; 300 MiB / s, para um tamanho de bloco de 1MiB). Portanto, presumi que findfosse possível ler o sistema de arquivos mais rapidamente ao gravar em um canal (ou arquivo), para que as duas operações que leem os caminhos do arquivo e gravem no console possam ser executadas em paralelo (o que, findcomo um processo de encadeamento único, não pode ser feito sozinho).

É findculpa

Comparando as duas chamadas

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

e

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

mostra que findfaz algo incrivelmente estúpido (seja lá o que for). Isso acaba sendo bastante incompetente na execução -name '*.txt'.

Pode depender da proporção de entrada / saída

Você pode pensar que find -nameganha se houver muito pouco para escrever. Mas isso só fica mais embaraçoso find. Ele perde, mesmo que não haja nada para gravar em arquivos de 200K (13 milhões de dados de canal) para grep:

time find /usr -name lwevhewoivhol

findpode ser tão rápido quanto grep, embora

Acontece que essa findestupidez com namenão se estende a outros testes. Use uma regex e o problema se foi:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Eu acho que isso pode ser considerado um bug. Alguém disposto a registrar um relatório de bug? Minha versão é find (GNU findutils) 4.6.0


Quão repetíveis são seus horários? Se você fez o -nameteste primeiro, pode ter sido mais lento devido ao conteúdo do diretório não estar em cache. (Ao testar -namee -regexacho que eles levam aproximadamente ao mesmo tempo, pelo menos uma vez o efeito cache foi levado em consideração Claro que pode ser apenas uma versão diferente. find...)
psmears

@psmears Obviamente, eu já fiz esses testes várias vezes. O problema do armazenamento em cache foi mencionado mesmo nos comentários da pergunta antes da primeira resposta. Minha findversão é find (GNU findutils) 4.6.0
Hauke ​​Laging

Por que é surpreendente que a adição -name '*.txt'diminua find? Ele precisa fazer um trabalho extra, testando cada nome de arquivo.
Barmar 04/10/19

@ Barmar Por um lado, esse trabalho extra pode ser feito extremamente rápido. Por outro lado, esse trabalho extra salva outro trabalho. findtem que escrever menos dados. E escrever em um tubo é uma operação muito mais lenta.
Hauke ​​Laging

A gravação em um disco é muito lenta, a gravação em um pipe não é tão ruim, apenas copia para um buffer do kernel. Observe que, no seu primeiro teste, escrever mais para /dev/nullusar de alguma forma menos tempo do sistema.
Barmar

0

Aviso : Suponho que você queira dizer find . -name filename(caso contrário, você está procurando coisas diferentes; find filenamena verdade, procura um caminho chamado nome do arquivo , que pode conter quase nenhum arquivo e, portanto, sair rapidamente).


Suponha que você tenha um diretório com cinco mil arquivos. Na maioria dos sistemas de arquivos, esses arquivos são realmente armazenados em uma estrutura em árvore , o que permite localizar rapidamente qualquer arquivo.

Então, quando você perguntar findpara localizar um arquivo cujo nome só exige a verificação, findirá pedir para que arquivo, e somente esse arquivo, para o sistema de arquivos subjacente, que irá ler muito poucas páginas a partir do armazenamento em massa. Portanto, se o sistema de arquivos valer a pena, essa operação será executada muito mais rápido do que percorrer a árvore inteira para recuperar todas as entradas.

Quando você pede algo simples, findno entanto, é exatamente isso que você faz, percorre a árvore inteira, lendo. Cada. Solteiro. Entrada. Com diretórios grandes, isso pode ser um problema (é exatamente o motivo pelo qual vários softwares, que precisam armazenar muitos arquivos em disco, criarão "árvores de diretório" com dois ou três componentes de profundidade: dessa forma, cada folha precisa conter apenas menos arquivos).


-2

Vamos supor que o arquivo / john / paul / george / ringo / beatles exista e o arquivo que você está procurando seja chamado de 'stones'

find / stones

find irá comparar 'beatles' com 'stones' e soltá-lo quando os 's' e 'b' não coincidirem.

find / | grep stones

Nesse caso, o find passará '/ john / paul / george / ringo / beatles' para grep e o grep terá que percorrer todo o caminho antes de determinar se é uma correspondência.

O grep está, portanto, fazendo muito mais trabalho e é por isso que leva mais tempo


1
Você já experimentou?
Hauke ​​Laging

3
O custo das comparações de strings (extremamente simples e barato) é completamente reduzido pelo custo de IO (ou apenas syscall, se armazenado em cache) das pesquisas de diretório.
Mat

grep não é uma comparação de strings, sua comparação de expressão regular, o que significa que ele precisa percorrer toda a string até encontrar uma correspondência ou chegar ao fim. As pesquisas de diretório são as mesmas, não importa o quê.
Paranoid

@Paranoid Hm, de que versão do find você está falando? Aparentemente, não é nada parecido com o achado no debian.
pipe
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.