Parece que find
seria necessário verificar se um determinado caminho corresponde a um arquivo ou diretório de qualquer maneira para percorrer recursivamente o conteúdo dos diretórios.
Aqui estão algumas motivações e o que eu fiz localmente para me convencer de que find . -type f
é realmente mais lento do que find .
. Ainda não procurei no código-fonte do GNU find.
Portanto, estou fazendo backup de alguns arquivos no meu $HOME/Workspace
diretório e excluindo arquivos que são dependências dos meus projetos ou arquivos de controle de versão.
Então eu executei o seguinte comando que executou rapidamente
% find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > ws-files-and-dirs.txt
find
canalizado grep
pode ter uma forma incorreta, mas parecia a maneira mais direta de usar um filtro regex negado.
O comando a seguir inclui apenas arquivos na saída de localização e levou muito mais tempo.
% find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > ws-files-only.txt
Eu escrevi um código para testar o desempenho desses dois comandos (com dash
e tcsh
, apenas para descartar quaisquer efeitos que o shell possa ter, mesmo que não deva haver). Os tcsh
resultados foram omitidos porque são essencialmente os mesmos.
Os resultados obtidos mostraram uma penalidade de desempenho de 10% para -type f
Aqui está a saída do programa mostrando a quantidade de tempo necessário para executar 1000 iterações de vários comandos.
% perl tester.pl
/bin/sh -c find Workspace/ >/dev/null
82.986582
/bin/sh -c find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
90.313318
/bin/sh -c find Workspace/ -type f >/dev/null
102.882118
/bin/sh -c find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
109.872865
Testado com
% find --version
find (GNU findutils) 4.4.2
Copyright (C) 2007 Free Software Foundation, Inc.
No Ubuntu 15.10
Aqui está o script perl que eu usei para fazer benchmarking
#!/usr/bin/env perl
use strict;
use warnings;
use Time::HiRes qw[gettimeofday tv_interval];
my $max_iterations = 1000;
my $find_everything_no_grep = <<'EOF';
find Workspace/ >/dev/null
EOF
my $find_everything = <<'EOF';
find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
EOF
my $find_just_file_no_grep = <<'EOF';
find Workspace/ -type f >/dev/null
EOF
my $find_just_file = <<'EOF';
find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
EOF
my @finds = ($find_everything_no_grep, $find_everything,
$find_just_file_no_grep, $find_just_file);
sub time_command {
my @args = @_;
my $start = [gettimeofday()];
for my $x (1 .. $max_iterations) {
system(@args);
}
return tv_interval($start);
}
for my $shell (["/bin/sh", '-c']) {
for my $command (@finds) {
print "@$shell $command";
printf "%s\n\n", time_command(@$shell, $command);
}
}
fonte
find
seria necessário verificar se um determinado caminho corresponde a um arquivo ou diretório de qualquer maneira para percorrer recursivamente o conteúdo dos diretórios. - teria que verificar se é um diretório, não precisaria verificar se é um arquivo. Existem outros tipos de entrada: pipes nomeados, links simbólicos, bloquear dispositivos especiais, soquetes ... Portanto, embora já tenha feito a verificação para ver se é um diretório, isso não significa que ele saiba se é um arquivo comum.-type f
e sem ele. Mas, pela primeira vez, o kernel Linux o carregou no cache e a primeira descoberta foi mais lenta.-type f
opção causadafind
chamarstat()
oufstat()
ou qualquer outra coisa, a fim de descobrir se o nome do arquivo corresponde a um arquivo, um diretório, um link simbólico, etc etc. Eu fiz umastrace
em umafind .
e umafind . -type f
e o traço era quase idêntico, diferindo apenas naswrite()
chamadas que continham nomes de diretório. Então, eu não sei, mas quero saber a resposta.time
comando interno para ver quanto tempo um comando leva para ser executado; você realmente não precisava escrever um script personalizado para testar.Respostas:
O GNU find tem uma otimização que pode ser aplicada,
find .
mas não afind . -type f
: se souber que nenhuma das entradas restantes em um diretório são diretórios, não se preocupa em determinar o tipo de arquivo (com astat
chamada do sistema), a menos que um dos critérios de pesquisa exige isso. A chamadastat
pode levar um tempo mensurável, pois as informações geralmente estão no inode, em um local separado no disco, e não no diretório que o contém.Como ele sabe? Como a contagem de links em um diretório indica quantos subdiretórios ele possui. Em sistemas de arquivos Unix típicos, a contagem de links de um diretório é 2 mais o número de diretórios: um para a entrada do diretório em seu pai, um para a
.
entrada e um para a..
entrada em cada subdiretório.A
-noleaf
opção informafind
para não aplicar essa otimização. Isso é útil sefind
for chamado em algum sistema de arquivos em que a contagem de links de diretório não siga a convenção do Unix.fonte
find
fonte, ele simplesmente usa as chamadasfts_open()
efts_read()
hoje em dia.stat
chamadas quando não precisa delas devido à contagem de links de diretório, e a-noleaf
opção está documentada no manual.stat
mesmo nafts...
versão - passa o sinalizador apropriado para afts_open
chamada. Mas o que não tenho certeza ainda é pertinente é a verificação do número de links. Em vez disso, verifica se o registro fts retornado possui um dos sinalizadores "diretório". Pode ser quefts_read
ele próprio verifique os links para definir esse sinalizador, masfind
não o faz. Você pode ver se sua versão dependefts
ligandofind --version
.find
teoricamente seria capaz de determinar quando todas as entradas de um diretório também são diretórios e usar essas informações?