Come contare il numero di file in ogni directory?


105

Sono in grado di elencare tutte le directory di

find ./ -type d

Ho tentato di elencare il contenuto di ciascuna directory e contare il numero di file in ciascuna directory utilizzando il seguente comando

find ./ -type d | xargs ls -l | wc -l

Ma questo somma il numero totale di righe restituite da

find ./ -type d | xargs ls -l

C'è un modo per contare il numero di file in ogni directory?


Stai cercando un modo per contare il numero di file in ciascuna delle sottodirectory direttamente sotto ./?
Tuxdude

5
Come è questa una domanda fuori tema? Vorrei vedere i commenti degli elettori stretti con ragione! Se questo è fuori tema, a cosa appartiene questo? super utente? Non credo proprio ..
InfantPro'Aravind '

6
shell-script, batch-script sono nell'ambito della programmazione!
InfantPro'Aravind '

Stavo per postare la soluzione Pythonic poi ho notato che la domanda è chiusa.
anatoly techtonik

ha votato per riaprirlo. Potrebbero esserci altre risposte che potrebbero essere utili in molte situazioni (inclusa la programmazione di script, motivo per cui sono arrivato a questa domanda).
lepe

Risposte:


110

Supponendo che tu abbia GNU find, lascia che trovi le directory e lascia che bash faccia il resto:

find . -type d -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

2
È solo una versione leggermente diversa dalla precedente, quindi: (suggerimento: è ordinato per nome ed è in csv) per x in find . -maxdepth 1 -type d | sort; do y = find $x | wc -l; echo $ x, $ y; fatto
pcarvalho

5
Grande! Mettendolo in una singola riga (quindi è comodo per l'uso diretto in shell):find . -type d -print0 | while read -d '' -r dir; do files=("$dir"/*); printf "%5d files in directory %s\n" "${#files[@]}" "$dir"; done
lucaferrario

13
Avevo bisogno di ottenere il numero di tutti i file (conteggio ricorsivo) in ogni sottodirectory. Questa modifica ti dà che: find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find $dir -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
OmidS

1
@Kory Lo farà:find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done | sort -rn -k1
OmidS

1
@OmidS Ottimo oneliner, ma $dirdovrebbe essere racchiuso tra virgolette nel tuo primo commento per gestire correttamente i nomi delle directory con spazi bianchi. :find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
Radek Daniluk

183

Stampa il conteggio dei file per directory per il livello di directory corrente:

du -a | cut -d/ -f2 | sort | uniq -c | sort -nr

9
Di gran lunga la soluzione migliore (e più elegante) se si vuole elencare ricorsivamente il numero di file nelle directory di primo livello.
itoctopus

13
Questo ha due problemi: conta un file per directory in più di quanto non sia effettivamente e fornisce una riga inutile contenente la dimensione della directory corrente come "1 size ". Entrambi possono essere risolti con du -a | sed '/.*\.\/.*\/.*/!d' | cut -d/ -f2 | sort | uniq -c. Aggiungi | sort -nrper ordinare in base al conteggio anziché al nome della directory.
dessert il

3
Vorrei sottolineare che funziona anche in OSX. (Il semplice copiare e incollare i consigli di Linux in una shell OSX di solito non funziona.)
Pistos

2
recupera le dimensioni non necessarie da du -a. Il modo migliore è usare il comando Trova. ma l'idea principale è esattamente la stessa :)
Znik

5
trova . -tipo f | tagliare -d / -f2 | ordina | uniq -c | sort -nr # risolve i problemi menzionati da dessert
jcomeau_ictx

28
find . -type f | cut -d/ -f2 | sort | uniq -c
  • find. -type f per trovare tutti gli elementi del tipo file
  • cut -d/ -f2 per ritagliare la loro cartella specifica
  • sort per ordinare l'elenco dei nomi delle cartelle
  • uniq -c per restituire il numero di volte in cui ogni nomecartella è stato contato

8
Questo è molto meglio della risposta accettata, poiché ottieni un riepilogo delle directory di primo livello!
Jason Floyd

3
Questa dovrebbe essere la risposta accettata. Semplice e comprensibile.
xssChauhan

1
La migliore risposta che dovrebbe essere accettata è questa.
loretoparisi

1
Semplice, elegante e perfetto per le mie esigenze.
RichR

Perfetto. E può essere esteso per contare su sottodirectory sostituendo gli specificatori di campo con un elenco di specificatori di campo. Ad esempio ,:find . -type f | cut -d/ -f2,3 | sort | uniq -c
algal

15

Puoi organizzare la ricerca di tutti i file, rimuovere i nomi dei file, lasciandoti una riga contenente solo il nome della directory per ogni file, e quindi contare il numero di volte in cui ogni directory appare:

find . -type f |
sed 's%/[^/]*$%%' |
sort |
uniq -c

L'unico problema è se hai nomi di file o nomi di directory contenenti un carattere di nuova riga, il che è abbastanza improbabile. Se devi davvero preoccuparti delle nuove righe nei nomi dei file o delle directory, ti suggerisco di trovarle e risolverle in modo che non contengano nuove righe (e persuadere silenziosamente il colpevole dell'errore dei loro metodi).


Se sei interessato al conteggio dei file in ciascuna sottodirectory della directory corrente, contando tutti i file in qualsiasi sottodirectory insieme ai file nella sottodirectory immediata, adattare il sedcomando per stampare solo la directory di primo livello:

find . -type f |
sed -e 's%^\(\./[^/]*/\).*$%\1%' -e 's%^\.\/[^/]*$%./%' |
sort |
uniq -c

Il primo modello cattura l'inizio del nome, il punto, la barra, il nome fino alla barra successiva e alla barra e sostituisce la linea solo con la prima parte, quindi:

./dir1/dir2/file1

è sostituito da

./dir1/

La seconda sostituzione acquisisce i file direttamente nella directory corrente; non hanno una barra alla fine e sono sostituiti da ./. L'ordinamento e il conteggio funzionano quindi solo sul numero di nomi.


1
Questo non restituisce nomi di directory che non contengono file. Non sono sicuro che sia necessario.
Austin Phillips

È vero, non è così. Non è particolarmente banale risolvere il problema, poiché non è garantito che i nomi di directory vuoti appaiano nell'output di find. Alcuni potrebbero: se c'è un file dir1/dir2/dir3/file1, ma dir1/dir2contiene solo sottodirectory (non file semplici), puoi dedurne la presenza. Ma se dir1/dir4non ha file, il nome semplicemente non viene visualizzato.
Jonathan Leffler

Risposta molto utile se vuoi solo vedere le sottodirectory della directory corrente.
xixixao

Sono appena passato per dire grazie. 3 anni dopo che questo è stato pubblicato, stavo cercando di contare le cartelle di 2 ° livello per cartella. Il tuo post mi ha salvato potenzialmente molte ore di armeggiare con sed, find e chissà cos'altro
Corvin

13

Ecco un modo per farlo, ma probabilmente non il più efficiente.

find -type d -print0 | xargs -0 -n1 bash -c 'echo -n "$1:"; ls -1 "$1" | wc -l' --

Fornisce un output come questo, con il nome della directory seguito dal conteggio delle voci in quella directory. Nota che il conteggio dell'output includerà anche voci di directory che potrebbero non essere quelle che desideri.

./c/fa/l:0
./a:4
./a/c:0
./a/a:1
./a/a/b:0

Sembra molto costoso per eseguire 3 comandi ( bash, ls, wc) per ogni directory trovata da find.
Jonathan Leffler

@JonathanLeffler d'accordo, da qui la prima riga della mia risposta. La tua soluzione è migliore.
Austin Phillips

bello questo è quello che sto cercando posso chiedere qual è il "-" alla fine?
una volta

1
@once - appartiene al comando bash che verrà generato da xargs. Da man bash, A -- signals the end of options and disables further option processing. In questo caso impedirebbe a un file con nome errato trovato come parte della ricerca di diventare parte dell'elaborazione degli argomenti per bash.
Austin Phillips

8

La soluzione di tutti gli altri ha uno svantaggio o un altro.

find -type d -readable -exec sh -c 'printf "%s " "$1"; ls -1UA "$1" | wc -l' sh {} ';'

Spiegazione:

  • -type d: siamo interessati alle directory.
  • -readable: Li vogliamo solo se è possibile elencare i file al loro interno. Nota che findcontinuerà a emettere un errore quando proverà a cercare più directory in esse, ma questo impedisce di chiamarle -exec.
  • -exec sh -c BLAH sh {} ';': per ogni directory, esegui questo frammento di script, con $0impostato su she $1impostato sul nome del file.
  • printf "%s " "$1": stampa in modo portatile e minimale il nome della directory, seguito solo da uno spazio, non da una nuova riga.
  • ls -1UA: elenca i file, uno per riga, in ordine di directory (per evitare di bloccare il pipe), escludendo solo le directory speciali .e..
  • wc -l: conta le righe

1
Modifica per mostrare il numero di file per primo sulla riga e per ordinare in base a loro:find -type d -readable -exec sh -c 'ls -1UA "$1" | wc -l | tr -d "\n" ; printf "\t%s\n" "$1" ' sh {} ';' | sort -n
Evgeni Sergeev

esegue la shell molte volte, quindi è lento e utilizza molto le risorse.
Znik

6

Versione leggermente modificata della risposta di Sebastian utilizzando findinvece di du(per escludere l'overhead relativo alla dimensione del file che dudeve essere eseguito e che non viene mai utilizzato):

 find ./ -mindepth 2 -type f | cut -d/ -f2 | sort | uniq -c | sort -nr

-mindepth 2viene utilizzato per escludere i file nella directory corrente. Se lo rimuovi, vedrai un gruppo di linee come le seguenti:

  234 dir1
  123 dir2
    1 file1
    1 file2
    1 file3
      ...
    1 fileN

(proprio come fa la duvariante -based)

Se è necessario contare anche i file nella directory corrente, utilizzare questa versione avanzata:

{ find ./ -mindepth 2 -type f | cut -d/ -f2 | sort && find ./ -maxdepth 1 -type f | cut -d/ -f1; } | uniq -c | sort -nr

L'output sarà come il seguente:

  234 dir1
  123 dir2
   42 .

5

Questo può essere fatto anche con il looping su ls invece di find

for f in */; do echo "$f -> $(ls $f | wc -l)"; done

Spiegazione:

for f in */; - loop su tutte le directory

do echo "$f -> - stampa ogni nome di directory

$(ls $f | wc -l) - chiama ls per questa directory e conta le righe


1
Questo non funziona correttamente se i nomi delle directory contengono spazi bianchi.
Xylol

Provafor f ./* ; do echo $f $(ls "$f" | wc -l); done
4ndt3s

3

Questo dovrebbe restituire il nome della directory seguito dal numero di file nella directory.

findfiles() {
    echo "$1" $(find "$1" -maxdepth 1 -type f | wc -l)
}

export -f findfiles

find ./ -type d -exec bash -c 'findfiles "$0"' {} \;

Output di esempio:

./ 6
./foo 1
./foo/bar 2
./foo/bar/bazzz 0
./foo/bar/baz 4
./src 4

L' export -foperazione è necessaria perché l' -execargomento findnon consente l'esecuzione di una funzione di bash a meno che non si richiama bash in modo esplicito, ed è necessario esportare la funzione definita nell'ambito corrente per la nuova shell in modo esplicito.


Questo sembra eccessivamente complicato. Mi sembra anche che dia conteggi cumulativi per una gerarchia di directory come ./dir1/dir2/dir3(contando i file in dir1e le sue sottodirectory tutti insieme, piuttosto che contare i file in dir1/dir2/dir3separatamente da quelli in dir1/dir2ed entrambi separatamente da quelli in /dir1).
Jonathan Leffler

Ho capito che era quello che voleva l'autore. In caso contrario, sono d'accordo che la risposta non è pertinente alla domanda.
Tuxdude

1
@JonathanLeffler - Ok, leggendo ancora una volta la domanda, ho capito che hai ragione - ho modificato la risposta di conseguenza.
Tuxdude

2

Ho combinato la risposta di @glenn jackman e la risposta di @ pcarvalho (nell'elenco dei commenti, c'è qualcosa di sbagliato nella risposta di pcarvalho perché la funzione di controllo dello stile extra del carattere ' ' '(backtick)).

Il mio script può accettare il percorso come un augument e ordinare l'elenco delle directory come ls -l, inoltre può gestire il problema dello "spazio nel nome del file" .

#!/bin/bash
OLD_IFS="$IFS"
IFS=$'\n'
for dir in $(find $1 -maxdepth 1 -type d | sort); 
do
    files=("$dir"/*)
    printf "%5d,%s\n" "${#files[@]}" "$dir"
done
FS="$OLD_IFS"

La mia prima risposta in stackoverflow, e spero che possa aiutare qualcuno ^ _ ^


1

trova . -type f -printf '% h \ n' | ordina | uniq -c

dà ad esempio:

  5 .
  4 ./aln
  5 ./aln/iq
  4 ./bs
  4 ./ft
  6 ./hot

0

Ho provato con alcuni degli altri qui, ma sono finito con le sottocartelle incluse nel conteggio dei file quando volevo solo i file. Viene stampato ./folder/path<tab>nnncon il numero di file, escluse le sottocartelle, per ciascuna sottocartella nella cartella corrente.

for d in `find . -type d -print` 
do 
  echo -e "$d\t$(find $d -maxdepth 1 -type f -print | wc -l)"
done

0

Un modo semplice per trovare ricorsivamente i file di un determinato tipo. In questo caso, file .jpg per tutte le cartelle nella directory corrente:

find . -name *.jpg -print | wc -l


0

Un comando miracolo super veloce, che attraversa ricorsivamente i file per contare il numero di immagini in una directory e organizzare l'output in base all'estensione dell'immagine:

find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'

Crediti: https://unix.stackexchange.com/a/386135/354980


0

Questo potrebbe essere un altro modo per navigare nelle strutture delle directory e fornire risultati approfonditi.

find . -type d  | awk '{print "echo -n \""$0"  \";ls -l "$0" | grep -v total | wc -l" }' | sh 

0

Ho modificato lo script in modo da escludere tutte le node_modulesdirectory all'interno di quella analizzata.

Questo può essere usato per verificare se il numero di file del progetto supera il numero massimo che il file watcher può gestire.

find . -type d ! -path "*node_modules*" -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

Per controllare il numero massimo di file che il tuo sistema può guardare:

cat /proc/sys/fs/inotify/max_user_watches

node_modules la cartella dovrebbe essere aggiunta ai percorsi esclusi dell'IDE / editor nei sistemi lenti e il conteggio degli altri file non dovrebbe idealmente superare il massimo (che può essere modificato).


-1

Questo darà il conteggio complessivo.

for file in */; do echo "$file -> $(ls $file | wc -l)"; done | cut -d ' ' -f 3| py --ji -l 'numpy.sum(l)'

No, non lo farà. Considererà solo un livello di sottodirectory.
Kusalananda
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.