Qual é a melhor maneira de contar o número de arquivos em um diretório?

11

Se a análise da saída de lsé perigosa, pois pode quebrar alguns caracteres descolados (espaços \n, ...), qual é a melhor maneira de saber o número de arquivos em um diretório?

Geralmente confio findpara evitar essa análise, mas, da mesma forma, find mydir | wc -lserá interrompida pelos mesmos motivos.

Estou trabalhando no Solaris agora, mas estou procurando uma resposta o mais portátil possível em diferentes unidades e diferentes tipos de conchas.

rahmu
fonte
3
Não tenho certeza se é uma duplicata, estou perdendo alguma coisa?
R17 #
1
Isso pode ser uma duplicata, mas não da pergunta indicada. findvai te número de arquivos de forma recursiva (use -maxdepth 1se você não quer isso. find mydir -maxdepth 1 -type f -printf \\n | wc -ldeve lidar com os caracteres especiais no nome do arquivo, como eles nunca são impressos em primeiro lugar.
Anthon

Respostas:

15

Que tal esse truque?

find . -maxdepth 1 -exec echo \; | wc -l

Tão portátil quanto finde wc.

rozcietrzewiacz
fonte
5
Isso não funciona (ele exibe n+1arquivos no meu sistema Debian). Também não filtra arquivos regulares.
Chris Baixo
4
Eu apenas dei um exemplo genérico. Ele faz o trabalho, mas como ele funciona depende de como você adaptar o findcomando para suas necessidades específicas. Sim, este inclui todos os diretórios, incluindo .(o que pode ser o motivo pelo qual você vê o resultado n+1).
rozcietrzewiacz
Eu gosto desse truque, muito inteligente; mas estou surpreso que não haja uma maneira simples e direta de fazer isso!
rahmu
3
@ChrisDown, o OP não especifica a filtragem de arquivos regulares, solicita o número de arquivos em um diretório. Para se livrar do problema n + 1, use find . -maxdepth 1 ! -name . -exec echo \; | wc -l; algumas versões mais antigas finddo não possuem -not.
Arcege
3
Observe que isso -maxdepthnão é padrão (uma extensão GNU agora também é suportada por algumas outras implementações).
Stéphane Chazelas
11

Com bash, sem utilitários externos, nem loops:

shopt -s dotglob
files=(*)
echo ${#files[@]}

Em ksh, substitua shopt -s dotglobpor FIGNORE=.?(.). No zsh, substitua-o por setopt glob_dotsou remova a shoptchamada e use files=(*(D)). (Ou simplesmente largue a linha se não quiser incluir arquivos de ponto.) De maneira portável, se você não se importa com arquivos de ponto:

set -- *
echo $#

Se você deseja incluir arquivos de ponto:

set -- *
if [ -e "$1" ]; then c=$#; else c=0; fi
set .[!.]*
if [ -e "$1" ]; then c=$((c+$#)); fi
set ..?*
if [ -e "$1" ]; then c=$((c+$#)); fi
echo $c
enzotib
fonte
2
O primeiro exemplo imprime 1para um diretório vazio quando nullglobnão está ativado. No zsh, a=(*(DN));echo ${#a}com o qualificador N( nullglob) não resulta em erro para um diretório vazio.
Nisetama
8
find . ! -name . -prune -print | grep -c /

Deve ser bastante portátil para os sistemas pós-anos 80.

Isso conta todas as entradas do diretório, exceto .e ..no diretório atual.

Para contar arquivos em subdiretórios também:

find .//. ! -name . | grep -c //

(esse deve ser portátil mesmo para o Unix V6 (1975), já que não é necessário -prune)

Stéphane Chazelas
fonte
Uma das raras respostas portáteis desta página, se não a única.
xhienne
Votou esta resposta ontem ontem, pois achei que também funciona bem para diretórios diferentes do diretório atual ( find dirname ! -name dirname -prune -print). Desde então, tenho me perguntado se existe algum motivo específico para usar em grep -c /vez de wc -l(o que provavelmente é mais comumente usado para contar).
Anthony Geoghegan
1
find dirname ! -name dirnamenão funciona se houver outros diretórios nomeados dirname. É melhor usar find dirname/. ! -name .. wc -lconta o número de linhas, os nomes de arquivos podem ser feitos de várias linhas, pois o caractere de nova linha é tão válido quanto qualquer outro em um nome de arquivo.
Stéphane Chazelas
6

Experimentar:

ls -b1A | wc -l

O -bterá caracteres não imprimíveis, -Airá mostrar todos os arquivos, exceto .e ..e um por linha (o padrão em uma tubulação, mas bom para ser explícito).

Contanto que incluamos linguagens de script de nível superior, aqui está uma linha em Python:

python -c 'import os; print len(os.listdir(os.sep))'

Ou com o 'find' completo:

python -c 'import os; print len([j for i in os.walk(os.sep) for j in i[1]+i[2]])'
Arcege
fonte
1

Yoc pode usar essa construção:

I=0; for i in * ; do ((I++)); done ; echo $I

Mas eu tenho medo, você pode errar como Argument list too long. no caso de ter muitos arquivos no diretório. No entanto, eu testei no diretório com 10 bilhões de arquivos e funcionou bem.

pressa
fonte
3
Isso não funcionará para arquivos ocultos, a menos que o shell esteja configurado para expandi-los *.
Lekensteyn
gnu find . -maxdepth 1 -type f | wc -l
Nikhil Mulley
4
@ Rush: este comando nunca deve aumentar a "lista de argumentos muito longa". Isso só acontece com o comando externo (portanto, nunca com for.
enzotib
1

Você já considerou o perl, que deve ser relativamente portátil?

Algo como:

use File::Find;

$counter = 0;

sub wanted { 
  -f && ++$counter
}

find(\&wanted, @directories_to_search);
print "$counter\n";
cjc
fonte
0

Tente isto => Usando ls com -i (por número de nó) & F (acrescenta nome do diretório com '/') opções.

ls -ilF | egrep -v '/' | wc -l
Saumil
fonte
0

Com uma perllinha (reformatada para facilitar a leitura):

perl -e 'opendir($dh, ".");
         while ( readdir($dh) ) {$count++};
         closedir $dh;
         print "$count\n";'

ou

perl -e 'opendir($dh, ".");
         @files = readdir($dh);
         closedir $dh;
         print $#files+1,"\n";'

Você pode usar perlfunções que modificam matrizes como grepou mapcom a segunda versão. Veja perldoc -f readdirum exemplo usando grep.

cas
fonte
0

A versão mais simples que eu uso o tempo todo e nunca tive problemas é: ls -b1 | wc -l

Peter
fonte
Você pode ter problemas se o nome do arquivo contiver um \nou outros caracteres descolados (sim, alguns departamentos permitem isso).
rahmu
1
Eu tentei isso explicitamente antes de postar minha resposta e não tive problemas com ela. Eu usei o gerenciador de arquivos nautilus para renomear um arquivo para conter \ n para tentar isso.
Peter
Você está certo, não funciona assim. Não sei o que fiz quando testei isso primeiro. Tentei novamente e atualizei minha resposta.
Peter
Não, o comando está OK, mas já existe uma solução semelhante e os arquivos ocultos não são contados.
xhienne
0

Além da findresposta baseada em proposta por Stéphane , aqui está uma resposta compatível com POSIX com base em ls:

ls -qf | tail -n +3 | wc -l
xhienne
fonte