Encontre diretórios contendo um certo número de arquivos

13

Esperava poder fazer isso com o findcomando, mas não vejo nenhum teste no manual para fazer o que quero. Gostaria de encontrar quaisquer diretórios no diretório de trabalho que contenham menos que, mais ou exatamente a contagem especificada.

find . -filecount +10 # any directory with more than 10 entries
find . -filecount 20 # any directory with exactly 20 entries

Mas, infelizmente, não existe essa opção.

Paul Ruane
fonte
tente algo como "ls -al | wc -l | grep"
Vanadis

Respostas:

16

Você pode tentar isso para obter os nomes dos subdiretórios e o número de arquivos / diretórios que eles contêm:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \;

Se você quiser fazer o mesmo para todos os subdiretórios (localização recursiva), use-o:

find . -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \;

Para selecionar os diretórios que possuem exatamente 10 arquivos:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | 
  awk '$NF==10'

10 ou mais:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | 
 awk '$NF>=10'

10 ou menos:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | 
 awk '$NF<=10'

Se você deseja manter apenas o nome do diretório (por exemplo, deseja canalizá-lo para outro processo a jusante, como @evilsoup sugeriu), você pode usar isso:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{}\t'; ls '{}' | wc -l" \; | 
 awk -F"\t" '$NF<=10{print $1}'
Terdon
fonte
1
Eu acho que pode ser útil incluir o comando awk para cortar a contagem de arquivos (ou seja, a última coluna delimitada por espaços em branco), caso o questionador queira canalizar a saída para outra coisa.
evilsoup
1
@evilsoup boa ideia, pronto.
terdon
Para suportar espaços em branco e caracteres especiais nos nomes de diretório; tente reverter o uso de aspas simples e duplas, como tal:find . -type d -exec bash -c 'echo -ne "{} "; ls "{}" | wc -l' \; | awk '$NF<=10'
Håvard Geithus
3

Para listar subdiretórios imediatos contendo exatamente $NUM arquivos.

find -maxdepth 2 -mindepth 2 -type f -printf '%h\0' | awk -v num="$NUM" 'BEGIN{RS="\0"} {array[$0]++} END{for (line in array) if (array[line]==num) printf "%s\n", line}'

Para listar subdiretórios imediatos contendo arquivos maiores que $NUM.

find -maxdepth 2 -mindepth 2 -type f -printf '%h\0' | awk -v num="$NUM" 'BEGIN{RS="\0"} {array[$0]++} END{for (line in array) if (array[line]>num) printf "%s\n", line}'

Para listar subdiretórios imediatos contendo menos de $NUMarquivos.

find -maxdepth 2 -mindepth 2 -type f -printf '%h\0' | awk -v num="$NUM" 'BEGIN{RS="\0"} {array[$0]++} END{for (line in array) if (array[line]<num) printf "%s\n", line}'

Os itens são finalizados com um caractere nulo \0; portanto, os nomes de arquivos que contêm novas linhas ou outros tipos de espaço em branco serão interpretados corretamente. O %himprime cada arquivo de dirname. awkdepois usa uma matriz para contar quantas vezes encontra cada diretório, imprimindo-o se as condições forem atendidas.

Observe que nenhum dos comandos mencionados acima exibirá diretórios contendo zero arquivos. Observe também que, por arquivo, estou me referindo a arquivos regulares, não a links, diretórios, soquetes, blocos, pipes nomeados, etc.

Eu tentei fazer isso da maneira mais simples possível. Se você deseja encontrar subdiretórios recursivos ou seus arquivos, é necessário um comando modificado. Existem muitas possibilidades para listar todas elas.

Seis
fonte
2

Tente o seguinte:

[`encontrar. | wc -l` -eq 10] && echo "Found"

[`encontrar. | wc -l` -gt 10] && eco "Encontrado"

[`encontrar. | wc -l` -lt 10] && eco "Encontrado"

Nestes exemplos, você pode verificar se o diretório CURRENT contém exatamente 10, mais que 10 e menos que 10 arquivos / diretórios. Se você precisar verificar vários diretórios, use loop.

setembro
fonte
Sua solução também conta com o diretório atual ( .), você pode querer modificar de acordo.
terdon
Gosto da orientação desta resposta (porque sou um glutão por fazer coisas no shell), mas é melhor você usar wc -l < <(printf %s\\n ./*)ou printf %s\\n ./* | wc -ldentro do teste para evitar uma findchamada desnecessária . Isso também evitará o problema que o @terdon observou, de incluir .no resultado. No entanto, também haveria o problema de ignorar arquivos começando com a .; Gostaria de resolver isso com shopt -s dotglob(para fazer bolhas corresponder arquivos começando com um ., mas não .ou ..).
evilsoup
@terdon Não é importante. Não é solução final, apenas exemplo, idéia. Você pode -1 ou alterar 10 para 11 na versão final.
setembro
Eu sei, e a ideia é boa, por isso fiz a sugestão.
terdon
@ Terdon. Obrigado. Pode haver muitos requisitos diferentes, como: Conte apenas arquivos, mas não diretórios, links ou links físicos. Contar ou não arquivos em subdiretórios. Conte os arquivos ocultos (como .bashrc) ... ... para que sua expressão seja muito lenta. :)
setembro