CentOS 5.9
Me deparei com um problema outro dia em que um diretório tinha muitos arquivos. Para contar, eu corrils -l /foo/foo2/ | wc -l
Acontece que havia mais de 1 milhão de arquivos em um único diretório (história longa - a causa raiz está sendo corrigida).
Minha pergunta é: existe uma maneira mais rápida de fazer a contagem? Qual seria a maneira mais eficiente de obter a contagem?
ls -l|wc -l
seria desligado por uma devido ao total de blocos na primeira linha dels -l
saída-A
flag.-l
também é problemático devido aos metadados do arquivo de leitura para gerar o formato de lista estendida. Forçar NÃO a-l
usar\ls
é uma opção muito melhor (-1
é assumida ao canalizar a saída). Consulte a resposta de Gilles para obter a melhor solução aqui.ls -l
não gera arquivos ocultos nem as entradas.
e..
.ls -a
A saída inclui arquivos ocultos, incluindo.
e..
enquanto als -A
saída inclui arquivos ocultos, excluindo.
e..
. Na resposta de Gilles, adotglob
opção bash shell faz com que a expansão inclua arquivos ocultos, excluindo.
e..
.Respostas:
Resposta curta:
(Isso inclui
.
e..
, portanto, subtraia 2.)Quando você lista os arquivos em um diretório, três coisas comuns podem acontecer:
ls
comando fazem isso.stat
para recuperar metadados sobre cada entrada de diretório, como se é um diretório.O número 3 é o mais caro, de longe, porque requer o carregamento de um inode para cada arquivo. Em comparação, todos os nomes de arquivos necessários para o nº 1 são armazenados compactamente em alguns blocos. O nº 2 desperdiça algum tempo de CPU, mas geralmente não é um disjuntor.
Se não houver novas linhas nos nomes dos arquivos, um simples
ls -A | wc -l
informa quantos arquivos existem no diretório. Lembre-se de que, se você tiver um apelido parals
, isso pode acionar uma chamada parastat
(por exemplo,ls --color
ouls -F
precisar saber o tipo de arquivo, o qual requer uma chamada parastat
); portanto, na linha de comando, liguecommand ls -A | wc -l
ou\ls -A | wc -l
para evitar um apelido.Se houver novas linhas no nome do arquivo, se as novas linhas estão listadas ou não, depende da variante Unix. O coreutils GNU e o BusyBox assumem o padrão de exibição
?
para uma nova linha, para que sejam seguros.Ligue
ls -f
para listar as entradas sem classificá-las (nº 2). Isso é ativado automaticamente-a
(pelo menos nos sistemas modernos). A-f
opção está no POSIX, mas com status opcional; a maioria das implementações suporta, mas não o BusyBox. A opção-q
substitui caracteres não imprimíveis, incluindo novas linhas por?
; é POSIX, mas não é suportado pelo BusyBox, portanto, omita-o se você precisar do suporte ao BusyBox às custas da contagem excessiva de arquivos cujo nome contém um caractere de nova linha.Se o diretório não tiver subdiretórios, a maioria das versões
find
não chamarástat
suas entradas (otimização de diretório em folha: um diretório com uma contagem de links 2 não pode ter subdiretórios, portanto,find
não é necessário procurar os metadados das entradas, a menos que condição-type
requerida). Assim,find . | wc -l
é uma maneira portátil e rápida de contar arquivos em um diretório, desde que o diretório não tenha subdiretórios e que nenhum nome de arquivo contenha uma nova linha.Se o diretório não possuir subdiretórios, mas os nomes dos arquivos puderem conter novas linhas, tente um destes (o segundo deve ser mais rápido se for suportado, mas pode não ser notável).
Por outro lado, não use
find
se o diretório tiver subdiretórios: inclusivefind . -maxdepth 1
chamastat
cada entrada (pelo menos com o GNU find e BusyBox find). Você evita a classificação (nº 2), mas paga o preço de uma pesquisa de inode (nº 3) que reduz o desempenho.No shell sem ferramentas externas, é possível executar a contagem dos arquivos no diretório atual com
set -- *; echo $#
. Isso perde arquivos de ponto (arquivos cujo nome começa com.
) e informa 1 em vez de 0 em um diretório vazio. Essa é a maneira mais rápida de contar arquivos em diretórios pequenos, pois não requer o início de um programa externo, mas (exceto no zsh) perde tempo para diretórios maiores devido à etapa de classificação (# 2).No bash, esta é uma maneira confiável de contar os arquivos no diretório atual:
No ksh93, esta é uma maneira confiável de contar os arquivos no diretório atual:
No zsh, esta é uma maneira confiável de contar os arquivos no diretório atual:
Se você tem o
mark_dirs
conjunto de opção, certifique-se de desligá-lo:a=(*(DNoN^M))
.Em qualquer shell POSIX, esta é uma maneira confiável de contar os arquivos no diretório atual:
Todos esses métodos classificam os nomes dos arquivos, exceto o zsh.
fonte
find -maxdepth 1
acompanha o ritmo facilmente\ls -U
desde que você não adicione nada como uma-type
declaração que precise fazer verificações adicionais. Você tem certeza que o GNU encontra realmente chamadasstat
? Mesmo a desaceleraçãofind -type
não é nada comparada com a quantidade dels -l
pântanos, se você retornar detalhes do arquivo. Por outro lado, o vencedor da velocidade clara estázsh
usando o globo sem classificação. (os globs classificados sãols
duas vezes mais lentos do que os não classificados são 2x mais rápidos). Gostaria de saber se os tipos de sistema de arquivos afetariam significativamente esses resultados.strace
. Isso só é verdade se o diretório tiver subdiretórios: caso contráriofind
, a otimização do diretório leaf entra em ação (mesmo sem-maxdepth 1
), eu deveria ter mencionado isso. Muitas coisas podem afetar o resultado, incluindo o tipo de sistema de arquivos (chamarstat
é muito mais caro em sistemas de arquivos que representam diretórios como listas lineares do que em sistemas de arquivos que representam diretórios como árvores), se todos os inodes foram criados juntos e, portanto, estão por perto no disco, cache frio ou quente, etc.ls -f
tem sido a maneira confiável de impedir a chamadastat
- isso geralmente é descrito hoje como "a saída não é classificada" (o que também causa) e inclui.
e..
.-A
e-U
não são opções padrão.\ls -afq *[0-9].pdb | wc -l
version sh (AT&T Research) 93u+ 2012-08-01
no meu sistema baseado no Debian,FIGNORE
não parece funcionar. As entradas.
e..
são incluídas na matriz resultanteÉ consideravelmente mais rápido na minha máquina, mas o
.
diretório local é adicionado à contagem.fonte
-type
parâmetrofind
deve ser mais rápido do quels
-mindepth 1
para omitir o próprio diretório.ls -1U
antes que o canal gaste um pouco menos de recursos, pois não tenta classificar as entradas do arquivo, apenas as lê conforme são classificadas na pasta em disco. Também produz menos produção, o que significa um pouco menos de trabalhowc
.Você também pode usar o
ls -f
que é mais ou menos um atalho parals -1aU
.Eu não sei se existe uma maneira eficiente de fazer isso através de um comando sem canalização.
fonte
Outro ponto de comparação. Apesar de não ser um shell oneliner, este programa C não faz nada supérfluo. Observe que os arquivos ocultos são ignorados para corresponder à saída de
ls|wc -l
(ls -l|wc -l
está desativada em um devido ao total de blocos na primeira linha de saída).fonte
readdir()
API stdio adiciona alguma sobrecarga e não fornece controle sobre o tamanho do buffer passado para a chamada do sistema subjacente (getdents
no Linux)Você poderia tentar
perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
Seria interessante comparar horários com seu cachimbo.
fonte
find -maxdepth 1 | wc -l
,\ls -AU | wc -l
e ozsh
glob não triagem e matriz de contagem com base). Em outras palavras, ele supera as opções com várias ineficiências, como classificar ou ler propriedades de arquivos estranhos. Atrevo-me a dizer uma vez que não ganhar nada também, não vale a pena usar mais de uma solução mais simples a menos que você esteja em perl já :).
e..
na contagem, portanto, é necessário subtrair dois para obter o número real de arquivos (e subdiretórios). No Perl moderno,perl -E 'opendir $dh, "."; $i++ while readdir $dh; say $i - 2'
faria isso.A partir desta resposta , posso pensar nesta como uma solução possível.
Copie o programa C acima no diretório em que os arquivos precisam ser listados. Em seguida, execute estes comandos:
fonte
ls -f
, não filtred_type
, apenas ligued->d_ino != 0
; 3) subtraia 2 para.
e..
.ls -f
.Uma solução apenas para o bash, que não requer nenhum programa externo, mas não sabe o quão eficiente:
fonte
Provavelmente, a maneira mais eficiente em termos de recursos não envolveria invocações de processos externos. Então eu apostaria em ...
fonte
Após corrigir o problema da resposta de @Joel, onde foi adicionado
.
como um arquivo:find /foo/foo2 -maxdepth 1 | tail -n +2 | wc -l
tail
simplesmente remove a primeira linha, o que significa que.
não é mais contado.fonte
wc
entrada não é muito eficiente, pois a sobrecarga aumenta linearmente em relação ao tamanho da entrada. Neste caso, por que não simplesmente diminuir a contagem final para compensar isso estar fora por um, que é uma operação de tempo constante:echo $(( $(find /foo/foo2 -maxdepth 1 | wc -l) - 1))
let count = $(find /foo/foo2 -maxdepth 1 | wc -l) - 2
os.listdir () em python pode fazer o trabalho por você. Ele fornece uma matriz do conteúdo do diretório, excluindo o especial '.' e arquivos '..'. Além disso, não é necessário se preocupar com arquivos especiais com caracteres especiais como '\ n' no nome.
a seguir é o tempo gasto pelo comando python acima comparado com o comando 'ls -Af'.
fonte
ls -1 | wc -l
vem imediatamente à minha mente. Sejals -1U
mais rápido do que ols -1
puramente acadêmico - a diferença deve ser insignificante, mas para diretórios muito grandes.fonte
Para excluir subdiretórios da contagem, aqui está uma variação da resposta aceita de Gilles:
A
$(( ))
expansão aritmética externa subtrai a saída do segundo$( )
subshell do primeiro$( )
. O primeiro$( )
é exatamente o de Gilles, de cima. O segundo$( )
gera a contagem de diretórios "vinculados" ao destino. Isso vemls -od
(substitua,ls -ld
se desejado), onde a coluna que lista a contagem de links físicos tem isso como um significado especial para diretórios. O "link" contagem inclui.
,..
e quaisquer subdiretórios.Não testei o desempenho, mas parece ser semelhante. Ele adiciona uma estatística do diretório de destino e alguma sobrecarga para o subshell e pipe adicionados.
fonte
Eu acho que echo * seria mais eficiente do que qualquer comando 'ls':
fonte
echo 'Hello World'|wc -w
produz2
.