Por que o `ls -l` conta mais arquivos que eu?

25

Aparentemente, não posso contar. Eu acho que existem três arquivos em/media

$ tree /media
/media
├── foo
├── onex
└── zanna
3 directories, 0 files

No entanto, ls -lencontra 12.

$ ls -l /media
total 12
drwxr-xr-x  2 root root 4096 Jul 31 20:57 foo
drwxrwxr-x  2 root root 4096 Jun 26 06:36 onex
drwxr-x---+ 2 root root 4096 Aug  7 21:17 zanna

E, se eu ls -laconseguir, apenas .e ..além do acima, mas a contagem étotal 20

Qual a explicação?

Zanna
fonte

Respostas:

33

O que 12você vê não é o número de arquivos, mas o número de blocos de disco consumidos.

De info coreutils 'ls invocation':

 For each directory that is listed, preface the files with a line
 `total BLOCKS', where BLOCKS is the total disk allocation for all
 files in that directory.  The block size currently defaults to 1024
 bytes, but this can be overridden (*note Block size::).  The
 BLOCKS computed counts each hard link separately; this is arguably
 a deficiency.

O total vai desde 12a 20quando você usa ls -laem vez de ls -lporque você está contando dois diretórios adicionais: .e ... Você está usando quatro blocos de disco para cada diretório (vazio), portanto, o total varia de 3 × 4 a 5 × 4. (Com toda a probabilidade, você está usando um bloco de disco de 4096 bytes para cada diretório; conforme a infopágina indica, o O utilitário não verifica o formato do disco, mas assume um tamanho de bloco, a 1024menos que seja instruído de outra forma.)

Se você deseja simplesmente obter o número de arquivos, tente algo como

ls | wc -l
user4556274
fonte
13
ls | wc -lfalhará se houver arquivos com uma nova linha no nome do arquivo. Isso é mais resiliente:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm
20
"se os nomes de arquivos têm uma nova linha neles" ... estremecimento
Petah
8
Como man lsserá dito, você pode evitar caracteres de controle com -b(escapa-os) ou -q(omite-os). Portanto, para contar, ls -1q | wc -lé seguro e preciso para mostrar arquivos não ocultos. ls -1qA | wc -lcontar arquivos ocultos (mas não .e ..). Estou usando em -1vez de, -lporque isso deve ser mais rápido.
Oli
18

user4556274 já respondeu o porquê . Minha resposta serve apenas para fornecer informações adicionais sobre como contar corretamente os arquivos.

Na comunidade Unix, o consenso geral é que analisar a saída de lsé uma péssima idéia , já que os nomes de arquivos podem conter caracteres de controle ou caracteres ocultos. Por exemplo, devido a um caractere de nova linha em um nome de arquivo, ls | wc -linformamos que há 5 linhas na saída de ls(que possui), mas, na realidade, existem apenas 4 arquivos no diretório.

$> touch  FILE$'\n'NAME                                                       
$> ls                                                                         
file1.txt  file2.txt  file3.txt  FILE?NAME
$> ls | wc -l
5

Método # 1: encontrar utilitário

O findcomando, normalmente usado para solucionar nomes de arquivos de análise, pode nos ajudar aqui imprimindo o número do inode . Seja um diretório ou um arquivo, ele possui apenas um número de inode exclusivo. Assim, usando -printf "%i\n"e excluindo .via -not -name ".", podemos ter uma contagem precisa dos arquivos. (Observe o uso de -maxdepth 1para impedir a descida recursiva em subdiretórios)

$> find  -maxdepth 1 -not -name "." -print                                    
./file2.txt
./file1.txt
./FILE?NAME
./file3.txt
$> find  -maxdepth 1 -not -name "." -printf "%i\n" | wc -l                    
4

Método # 2: globstar

Maneira simples, rápida e principalmente portátil:

$ set -- * 
$ echo $#
228

setO comando é usado para definir parâmetros posicionais do shell (as $<INTEGER>variáveis, como em echo $1). Isso geralmente é usado para solucionar as /bin/shlimitações de matrizes ausentes. Uma versão que realiza verificações extras pode ser encontrada na resposta de Gille no Unix e Linux.

Em shells que suportam matrizes, como bash, podemos usar

items=( dir/* )
echo ${#items[@]}

como proposto por steeldriver nos comentários .

Truque semelhante ao findmétodo usado wce globstar pode ser usado statpara contar números de inodes por linha:

$> LC_ALL=C stat ./* --printf "%i\n" | wc -l                                          
4

Uma abordagem alternativa é usar um curinga no forloop. (Observe, esse teste usa um diretório diferente para testar se essa abordagem é dividida em subdiretórios, o que não ocorre - 16 é o número verificado de itens no meu ~/bin)

$> count=0; for item in ~/bin/* ; do count=$(($count+1)) ; echo $count ; done | tail -n 1                                
16

Método # 3: outros idiomas / intérpretes

O Python também pode lidar com nomes de arquivos problemáticos, imprimindo o tamanho de uma lista, de acordo com minha os.listdir()função (que não é recursiva, e listará apenas os itens no diretório como argumento).

$> python -c "import os ; print os.listdir('.')"                              
['file2.txt', 'file1.txt', 'FILE\nNAME', 'file3.txt']
$>  python -c "import os ; print(len(os.listdir('.')))"                    
4

Veja também

Sergiy Kolodyazhnyy
fonte
2
No bash, outra opção seria usar uma matriz, por exemplo items=( dir/* ); echo ${#items[@]}(adicionando shopt -s dotglobpara incluir arquivos ocultos).
steeldriver
1
A impressão de números de inode facilita a filtragem de links físicos, se desejado, com find | sort -u | wc -l.
Peter Cordes
@steeldriver: Acho improvável que o método bash-array seja mais rápido. Se você quiser que seja recursivo, precisará usar items=( dir/** )(with shopt -s globstar), mas o bash não tira proveito de metadados extras do readdir, portanto, ele classifica cada entrada de diretório para ver se é um diretório em si. Muitos sistemas de arquivos armazenam o tipo de arquivo na entrada do diretório, para que o readdir possa retorná-lo sem acessar os inodes. (por exemplo, o XFS não padrão mais recente possui isso, e acho que o ext4 o possui há mais tempo.) Se você straceencontrar, verá muito menos statchamadas do sistema do que o bash.
Peter Cordes
2
Por que não usar apenas print(len(os.listdir('.')))? Menos caracteres para digitar e também evita o acesso a atributos com sublinhado duplo.
precisa saber é
1
@edwinksl editado, thx
Sergiy Kolodyazhnyy