¿Por qué `ls -l` cuenta más archivos que yo?

25

Aparentemente no puedo contar. Creo que hay tres archivos en/media

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

Sin embargo, ls -lencuentra 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

Y, si lo hago ls -la, obtengo solo .y ..además de lo anterior, pero el recuento estotal 20

¿Cuál es la explicación?

Zanna
fuente

Respuestas:

33

Lo 12que ves no es la cantidad de archivos, sino la cantidad de bloques 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.

El total va de 12a 20cuando lo usas en ls -lalugar de ls -lporque estás contando dos directorios adicionales: .y ... Está utilizando cuatro bloques de disco para cada directorio (vacío), por lo que su total va de 3 × 4 a 5 × 4. (Con toda probabilidad, está utilizando un bloque de disco de 4096 bytes para cada directorio; como infoindica la página, el la utilidad no verifica el formato del disco, pero asume un tamaño de bloque de a 1024menos que se indique lo contrario)

Si desea obtener simplemente la cantidad de archivos, puede intentar algo como

ls | wc -l
usuario4556274
fuente
13
ls | wc -lfallará si hay archivos con una nueva línea en el nombre del archivo. Esto es más resistente:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm
20
"si los nombres de archivo tienen una nueva línea" ... estremecimiento
Petah
8
Como se man lsle dirá, puede evitar los caracteres de control con -b(los escapa) u -q(los omite). Entonces, para contar, ls -1q | wc -les seguro y preciso para mostrar archivos no ocultos. ls -1qA | wc -lpara contar archivos ocultos (pero no .y ..). Estoy usando en -1lugar de -lporque eso debería ser más rápido.
Oli
18

user4556274 ya ha respondido el por qué . Mi respuesta solo sirve para proporcionar información adicional sobre cómo contar correctamente los archivos.

En la comunidad Unix, el consenso general es que analizar la salida de lses una muy, muy mala idea , ya que los nombres de archivo pueden contener caracteres de control o caracteres ocultos. Por ejemplo, debido a un carácter de nueva línea en un nombre de archivo, nos hemos ls | wc -ldicho que hay 5 líneas en la salida de ls(que sí tiene), pero en realidad solo hay 4 archivos en el directorio.

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

Método # 1: encontrar utilidad

El findcomando, que generalmente se usa para trabajar al analizar nombres de archivos, puede ayudarnos aquí imprimiendo el número de inodo . Ya sea un directorio o un archivo, solo tiene un número de inodo único. Por lo tanto, utilizando -printf "%i\n"y excluyendo a .través de -not -name "."podemos tener un recuento preciso de los archivos. (Tenga en cuenta el uso de -maxdepth 1para evitar el descenso recursivo en subdirectorios)

$> 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

Forma simple, rápida y mayormente portátil:

$ set -- * 
$ echo $#
228

setEl comando se utiliza para establecer los parámetros posicionales del shell (las $<INTEGER>variables, como en echo $1). Esto a menudo se usa para evitar la /bin/shlimitación de la falta de matrices. Se puede encontrar una versión que realiza verificaciones adicionales en la respuesta de Gille en Unix y Linux.

En shells que admiten matrices, como bash, podemos usar

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

según lo propuesto por steeldriver en los comentarios .

Truco similar al findmétodo que se usó wcy globstar se puede usar statpara contar números de inodo por línea:

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

Un enfoque alternativo es usar un comodín en forbucle. (Tenga en cuenta que esta prueba usa un directorio diferente para probar si este enfoque desciende a subdirectorios, lo que no es así: 16 es el número verificado de elementos en mi ~/bin)

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

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

Python también puede manejar nombres de archivos problemáticos imprimiendo la longitud de una lista dada mi os.listdir()función (que no es recursiva, y solo listará elementos en el directorio dado 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

Ver también

Sergiy Kolodyazhnyy
fuente
2
En bash, otra opción sería usar una matriz, por ejemplo items=( dir/* ); echo ${#items[@]}(agregar shopt -s dotglobpara incluir archivos ocultos).
steeldriver
1
La impresión de números de inodo facilita el filtrado de enlaces duros si lo desea, con find | sort -u | wc -l.
Peter Cordes
@steeldriver: creo que es poco probable que el método bash-array sea más rápido. Si desea que sea recursivo, debe usar items=( dir/** )(con shopt -s globstar), pero bash no aprovecha los metadatos adicionales de readdir, por lo que registra cada entrada de directorio para ver si es un directorio en sí. Muchos sistemas de archivos almacenan el tipo de archivo en la entrada del directorio, por lo que readdir puede devolverlo sin acceder a los inodos. (por ejemplo, el último XFS no predeterminado tiene esto, y creo que ext4 lo ha tenido por más tiempo). Si lo straceencuentra, verá muchas menos statllamadas al sistema que strah bash.
Peter Cordes
2
¿Por qué no solo usar print(len(os.listdir('.')))? Menos caracteres para escribir y también evita el acceso a atributos doblemente subrayados.
edwinksl
1
@edwinksl editado, gracias
Sergiy Kolodyazhnyy