Como contar recursivamente para o número de arquivos em vários diretórios?

4

Eu tenho um diretório, contendo muitos arquivos e diretórios.

Estou tentando obter o número de arquivos (e diretórios) contidos recursivamente em todos os diretórios.

Eu tentei a seguinte abordagem:

for dir in $(find -maxdepth 1 -type d); do echo "$dir"; echo find "$dir" | wc -l; done

Mas isso retorna "1" como resultado para cada diretório.

Eu sei que há várias outras perguntas com uma pergunta semelhante, mas eu realmente gostaria de saber qual é o meu erro no código acima.

    
por Majiy 03.04.2013 / 11:22

5 respostas

6

Uma solução GNU ( bash , wc e find ) que funciona com qualquer caminho , mesmo aqueles contendo espaços, novas linhas ou começando com um traço:

shopt -s nullglob
for dir in ./*/
do
    printf '%s\n' "$dir"
    find "$dir" -mindepth 1 -printf x | wc --chars
done

Explicação:

  • A opção nullglob evita erros se ./ não contiver diretórios.
  • O ./ no diretório glob garante que os nomes de arquivos que começam com um traço (" - ") não atrapalhem echo ou find .
  • A barra no final da glob garante que apenas diretórios sejam processados.
  • -mindepth 1 evita contar o próprio diretório.
  • Se você deseja incluir diretórios que começam com um ponto no nível superior, execute shopt -s dotglob antes do loop for .
por 03.04.2013 / 11:49
1

Aqui está outro método com bash4 +. Observe que segue links simbólicos e não inclui . e .. ao contrário da resposta de l0b0 (que pode ou não ser o que você deseja):

(
    shopt -s dotglob globstar nullglob
    for dir in */; do
        set -- "$dir"/**/*
        printf '%s: %d\n' "$dir" "$#"
    done
)
    
por 03.04.2013 / 12:08
1

$(find -maxdepth 1 -type d) exibe a lista de diretórios no diretório atual. A menos que existam diretórios cujo nome comece com . , essa é uma maneira complexa de escrever */ . Também não é confiável: ele só funciona se nenhum dos nomes de diretório contiver espaços em branco ou caracteres globbing \[?* . Isso ocorre porque o resultado da substituição do comando $(…) é dividido em palavras separadas onde quer que haja um caractere de espaço em branco, e cada palavra é interpretada como glob (padrão curinga de nome de arquivo). Você pode evitar esse comportamento colocando a substituição do comando entre aspas duplas ( "$()" ), mas a lista na qual o loop será repetido conterá um único elemento que é a concatenação dos nomes de diretório separados por novas linhas.

Observe esta regra de programação shell: sempre coloca aspas duplas em torno de substituições de variáveis e substituições de comandos ( "$foo" , "$(foo)" ) a menos que você saiba que precisa deixar as aspas duplas e você entende como é seguro deixá-los de fora.

O outro problema com o seu script é simples: echo find "$dir" sempre imprime uma linha; você quis dizer find "$dir" .

for dir in */; do
  echo "$dir"
  find "$dir" | wc -l
done

Observe que isso só funciona se nenhum arquivo dentro dessa árvore contiver novas linhas. Se puderem, você poderá fazer com que o comando find imprima algo confiável. Com o GNU find (ou seja, no Linux ou Cygwin não integrado):

for dir in */; do
  echo "$dir"
  find "$dir" -printf a | wc -c
done

Portável:

for dir in */; do
  echo "$dir"
  find "$dir" -exec printf %c {} + | wc -c
done
    
por 04.04.2013 / 03:31
0

Uma variante pequena e ligeiramente mais rápida das soluções portáteis Gilles seria:

for dir in */; do
  echo "$dir"
  #find "$dir" -exec printf %c {} + | wc -c
  find "$dir" -print0 | tr -dc '
for dir in */; do
  echo "$dir"
  #find "$dir" -exec printf %c {} + | wc -c
  find "$dir" -print0 | tr -dc '%pre%' | wc -c
done
' | wc -c done
    
por 05.04.2013 / 12:26
0

Usando o GNU Parallel, será assim:

parallel -0 --tag  'find {} |wc -l' ::: */

Ele executará um find|wc por CPU em paralelo. Dependendo do sistema de armazenamento, o paralelismo pode aumentar ou diminuir a velocidade - a única maneira de saber é testá-lo. O número de processos pode ser ajustado com -j .

O GNU Parallel é um paralelizador geral e facilita a execução de trabalhos em paralelo na mesma máquina ou em várias máquinas para as quais você tem acesso ssh.

Se você tem 32 tarefas diferentes que você quer rodar em 4 CPUs, uma maneira direta de paralelizar é rodar 8 tarefas em cada processador:

OGNUParallelgeraumnovoprocessoquandoumtermina-mantendoasCPUsativaseeconomizandotempo:

Instalação

Se o GNU Parallel não for empacotado para sua distribuição, você poderá fazer uma instalação pessoal, que não requer acesso root. Isso pode ser feito em 10 segundos ao fazer isso:

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash

Para outras opções de instalação, consulte o link

Saiba mais

Veja mais exemplos: link

Assista aos vídeos de introdução: link

Percorra o tutorial: link

Inscreva-se na lista de e-mail para obter suporte: link

    
por 11.05.2015 / 20:29

Tags