Perché `ls -l` conta più file di me?


25

Apparentemente non posso contare. Penso che ci siano tre file in/media

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

Tuttavia, ls -ltrova 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 lo faccio, ls -laottengo solo .e ..in aggiunta a quanto sopra, ma il conteggio ètotal 20

Qual è la spiegazione?

Risposte:


33

La 12si vede non è il numero di file, ma il numero dei blocchi del disco.

Da 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.

Il totale va da 12a 20quando si utilizza ls -laanziché ls -lperché si contano due directory aggiuntive: .e ... Stai usando quattro blocchi di dischi per ogni directory (vuota), quindi il tuo totale va da 3 × 4 a 5 × 4. (Con ogni probabilità, stai usando un blocco di disco di 4096 byte per ogni directory; come infoindica la pagina, il l'utilità non controlla il formato del disco, ma presuppone una dimensione del blocco 1024se non diversamente specificato.)

Se vuoi semplicemente ottenere il numero di file, potresti provare qualcosa del genere

ls | wc -l

13
ls | wc -lfallirà se ci sono file con una nuova riga nel nome del file. Questo è più resistente:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm,

20
"se i nomi dei file contengono una nuova riga" ... brivido
Petah,

8
Come man lsti diremo, puoi evitare i caratteri di controllo con -b(li sfugge) o -q(li omette). Quindi, per il conteggio, ls -1q | wc -lè sicuro e preciso per mostrare file non nascosti. ls -1qA | wc -lper contare i file nascosti (ma non .e ..). Sto usando -1invece -lperché dovrebbe essere più veloce.
Oli

18

user4556274 ha già risposto al perché . La mia risposta serve solo a fornire ulteriori informazioni su come contare correttamente i file.

Nella comunità Unix il consenso generale è che l' analisi dell'output di lsè una pessima idea , dal momento che i nomi dei file possono contenere caratteri di controllo o caratteri nascosti. Ad esempio, a causa di un carattere di nuova riga in un nome file, abbiamo ls | wc -ldetto che ci sono 5 righe nell'output di ls(che ha), ma in realtà ci sono solo 4 file nella directory.

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

Metodo n. 1: trova utilità

Il findcomando, che in genere viene utilizzato per aggirare l'analisi dei nomi dei file, può aiutarci qui stampando il numero dell'inode . Che si tratti di una directory o di un file, ha solo un numero di inode univoco. Pertanto, usando -printf "%i\n"ed escludendo .via -not -name "."possiamo avere un conteggio accurato dei file. (Notare l'uso di -maxdepth 1per impedire la discesa ricorsiva nelle sottodirectory)

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

Metodo n. 2: globstar

Modo semplice, rapido e per lo più portatile:

$ set -- * 
$ echo $#
228

setIl comando viene utilizzato per impostare i parametri posizionali della shell (le $<INTEGER>variabili, come in echo $1). Questo è spesso usato per aggirare la /bin/shlimitazione delle matrici mancanti. Una versione che esegue controlli extra può essere trovata nella risposta di Gille su Unix e Linux.

In shell che supportano array, come ad esempio bash, possiamo usare

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

come proposto da steeldriver nei commenti .

Trucco simile al findmetodo utilizzato wce globstar con cui statpoter contare i numeri di inode per riga:

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

Un approccio alternativo consiste nell'utilizzare un carattere jolly in forloop. (Nota, questo test utilizza una directory diversa per verificare se questo approccio scende nelle sottodirectory, che non ha - 16 è il numero verificato di elementi nel mio ~/bin)

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

Metodo n. 3: altre lingue / interpreti

Python può anche gestire nomi di file problematici stampando la lunghezza di un elenco dato la mia os.listdir()funzione (che non è ricorsiva e elencherà solo gli elementi nella directory indicati come argomento).

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

Guarda anche


2
In bash, un'altra opzione sarebbe quella di utilizzare un array, ad esempio items=( dir/* ); echo ${#items[@]}(aggiungendo shopt -s dotglobper includere file nascosti).
Steeldriver,

1
La stampa dei numeri di inode semplifica il filtraggio dei collegamenti fisici, se lo si desidera, con find | sort -u | wc -l.
Peter Cordes,

@steeldriver: penso che il metodo bash-array difficilmente sia più veloce. Se vuoi che sia ricorsivo, devi usare items=( dir/** )(con shopt -s globstar), ma bash non sfrutta i metadati extra di readdir, quindi statistiche ogni voce della directory per vedere se è una directory stessa. Molti filesystem memorizzano il tipo di file nella voce della directory, quindi readdir può restituirlo senza accedere agli inode. (ad esempio l'ultimo XFS non predefinito ha questo, e penso che ext4 lo abbia avuto più a lungo.) Se lo stracetrovi, vedrai molte meno statchiamate di sistema rispetto a bash di stringhe.
Peter Cordes,

2
Perché non usare solo print(len(os.listdir('.')))? Meno caratteri da digitare ed evita anche di accedere ad attributi doppiamente sottolineati.
edwinksl,

1
@edwinksl modificato, grazie
Sergiy Kolodyazhnyy l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.