Conteggio veloce dei file Linux per un gran numero di file


137

Sto cercando di capire il modo migliore per trovare il numero di file in una directory particolare quando c'è un numero molto grande di file (> 100.000).

Quando ci sono molti file, l'esecuzione ls | wc -lrichiede molto tempo per essere eseguita. Credo che questo sia perché sta restituendo i nomi di tutti i file. Sto cercando di occupare il meno possibile l'IO del disco.

Ho sperimentato invano alcuni script di shell e Perl. Qualche idea?


2
assicurati che il tuo "ls" sia / usr / bin / ls e non un alias di qualcosa di più elaborato.
Glenn Jackman,

Domanda simile con risposte interessanti qui: serverfault.com/questions/205071/…
aidan

Vale la pena sottolineare che la maggior parte se non tutte le soluzioni presentate a questa domanda non sono specifiche per Linux , ma sono piuttosto generali per tutti i sistemi * NIX. Forse la rimozione del tag "Linux" è appropriata.
Christopher Schultz,

Risposte:


189

Per impostazione predefinita lsordina i nomi, il che può richiedere del tempo se ce ne sono molti. Inoltre non ci sarà output fino a quando tutti i nomi non saranno letti e ordinati. Utilizzare l' ls -fopzione per disattivare l'ordinamento.

ls -f | wc -l

Si noti che questo permetterà anche -a, così ., ..e altri file che iniziano con .saranno conteggiati.


11
+1 E pensavo di sapere tutto quello che c'era da sapere ls.
mob,

5
ZOMG. L'ordinamento di 100.000 righe non è nulla, rispetto alla stat()chiamata lssu ogni file. findnon funziona stat()quindi più velocemente.
Dummy00001,

12
ls -fnon fa stat()neanche. Ma ovviamente entrambi lse findchiamano stat()quando vengono utilizzate determinate opzioni, come ls -lo find -mtime.
mark4o

7
Per il contesto, sono stati necessari 1-2 minuti per contare 2,5 milioni di jpg su una casella Slicehost di dimensioni ridotte.
philfreo,

6
Se vuoi aggiungere sottodirectory al conteggio, fails -fR | wc -l
Ryan Walls il

62

Il modo più veloce è un programma appositamente costruito, come questo:

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dir;
    struct dirent *ent;
    long count = 0;

    dir = opendir(argv[1]);

    while((ent = readdir(dir)))
            ++count;

    closedir(dir);

    printf("%s contains %ld files\n", argv[1], count);

    return 0;
}

Dai miei test senza riguardo alla cache, ho eseguito ciascuno di questi circa 50 volte ciascuno contro la stessa directory, ripetutamente, per evitare l'inclinazione dei dati basata sulla cache e ho ottenuto approssimativamente i seguenti numeri di prestazioni (in tempo reale):

ls -1  | wc - 0:01.67
ls -f1 | wc - 0:00.14
find   | wc - 0:00.22
dircnt | wc - 0:00.04

Quest'ultimo dircnt, è il programma compilato dalla fonte sopra.

MODIFICA 26/09/2016 2016

A causa della grande richiesta, ho riscritto questo programma in modo ricorsivo, quindi cadrà nelle sottodirectory e continuerà a contare i file e le directory separatamente.

Dal momento che è chiaro che alcune persone vogliono sapere come fare tutto questo, ho molti commenti nel codice per cercare di rendere ovvio cosa sta succedendo. L'ho scritto e testato su Linux a 64 bit, ma dovrebbe funzionare su qualsiasi sistema compatibile con POSIX, incluso Microsoft Windows. Le segnalazioni di bug sono benvenute; Sono felice di aggiornarlo se non riesci a farlo funzionare su AIX o OS / 400 o altro.

Come puoi vedere, è molto più complicato dell'originale e necessariamente così: almeno una funzione deve esistere per essere chiamata in modo ricorsivo a meno che tu non voglia che il codice diventi molto complesso (es. Gestire uno stack di sottodirectory ed elaborarlo in un singolo loop). Dal momento che dobbiamo verificare i tipi di file, entrano in gioco le differenze tra diversi sistemi operativi, librerie standard, ecc., Quindi ho scritto un programma che cerca di essere utilizzabile su qualsiasi sistema in cui verrà compilato.

Il controllo degli errori è molto limitato e la countfunzione stessa non segnala errori. Le uniche chiamate che possono davvero fallire sono opendire stat(se non sei fortunato e hai un sistema in cui direntcontiene già il tipo di file). Non sono paranoico nel controllare la lunghezza totale dei nomi dei percorsi dei subdir, ma teoricamente il sistema non dovrebbe consentire alcun nome di percorso più lungo di PATH_MAX. Se ci sono preoccupazioni, posso risolverlo, ma è solo più codice che deve essere spiegato a qualcuno che sta imparando a scrivere C. Questo programma vuole essere un esempio di come immergersi in sottodirectory ricorsivamente.

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>

#if defined(WIN32) || defined(_WIN32) 
#define PATH_SEPARATOR '\\' 
#else
#define PATH_SEPARATOR '/' 
#endif

/* A custom structure to hold separate file and directory counts */
struct filecount {
  long dirs;
  long files;
};

/*
 * counts the number of files and directories in the specified directory.
 *
 * path - relative pathname of a directory whose files should be counted
 * counts - pointer to struct containing file/dir counts
 */
void count(char *path, struct filecount *counts) {
    DIR *dir;                /* dir structure we are reading */
    struct dirent *ent;      /* directory entry currently being processed */
    char subpath[PATH_MAX];  /* buffer for building complete subdir and file names */
    /* Some systems don't have dirent.d_type field; we'll have to use stat() instead */
#if !defined ( _DIRENT_HAVE_D_TYPE )
    struct stat statbuf;     /* buffer for stat() info */
#endif

/* fprintf(stderr, "Opening dir %s\n", path); */
    dir = opendir(path);

    /* opendir failed... file likely doesn't exist or isn't a directory */
    if(NULL == dir) {
        perror(path);
        return;
    }

    while((ent = readdir(dir))) {
      if (strlen(path) + 1 + strlen(ent->d_name) > PATH_MAX) {
          fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + 1 + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
          return;
      }

/* Use dirent.d_type if present, otherwise use stat() */
#if defined ( _DIRENT_HAVE_D_TYPE )
/* fprintf(stderr, "Using dirent.d_type\n"); */
      if(DT_DIR == ent->d_type) {
#else
/* fprintf(stderr, "Don't have dirent.d_type, falling back to using stat()\n"); */
      sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
      if(lstat(subpath, &statbuf)) {
          perror(subpath);
          return;
      }

      if(S_ISDIR(statbuf.st_mode)) {
#endif
          /* Skip "." and ".." directory entries... they are not "real" directories */
          if(0 == strcmp("..", ent->d_name) || 0 == strcmp(".", ent->d_name)) {
/*              fprintf(stderr, "This is %s, skipping\n", ent->d_name); */
          } else {
              sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
              counts->dirs++;
              count(subpath, counts);
          }
      } else {
          counts->files++;
      }
    }

/* fprintf(stderr, "Closing dir %s\n", path); */
    closedir(dir);
}

int main(int argc, char *argv[]) {
    struct filecount counts;
    counts.files = 0;
    counts.dirs = 0;
    count(argv[1], &counts);

    /* If we found nothing, this is probably an error which has already been printed */
    if(0 < counts.files || 0 < counts.dirs) {
        printf("%s contains %ld files and %ld directories\n", argv[1], counts.files, counts.dirs);
    }

    return 0;
}

MODIFICA 2017-01-17

Ho incorporato due modifiche suggerite da @FlyingCodeMonkey:

  1. Usa lstatinvece di stat. Ciò cambierà il comportamento del programma se nella directory che si sta eseguendo la scansione sono presenti directory con collegamenti simbolici. Il comportamento precedente era che la sottodirectory (collegata) avrebbe avuto il suo conteggio dei file aggiunto al conteggio complessivo; il nuovo comportamento è che la directory collegata verrà conteggiata come un singolo file e il suo contenuto non verrà conteggiato.
  2. Se il percorso di un file è troppo lungo, verrà emesso un messaggio di errore e il programma si arresterà.

MODIFICA 2017-06-29

Con un po 'di fortuna, questa sarà l' ultima modifica di questa risposta :)

Ho copiato questo codice in un repository GitHub per rendere un po 'più semplice ottenere il codice (invece di copiare / incollare, puoi semplicemente scaricare il sorgente ), inoltre rende più semplice per chiunque suggerire una modifica inviando un pull -Richiesta da GitHub.

La fonte è disponibile con licenza Apache 2.0. Patch * benvenuto!


  • "patch" è ciò che gli anziani come me chiamano "richiesta pull".

2
Semplicemente fantastico! Grazie! E per chi non lo sapesse: è possibile compilare il codice sopra nel terminale: gcc -o dircnt dircnt.ce usare è così./dircnt some_dir
aesede

C'è un modo semplice per rendere questo ricorsivo?
ck_

@ck_ Certo, questo può essere facilmente reso ricorsivo. Hai bisogno di aiuto con la soluzione o vuoi che scriva tutto?
Christopher Schultz,

1
@ChristopherSchultz, i benchmark che hai pubblicato sopra - quanto era grande la directory in questione?
Dom Vinyard,

1
Volevo davvero usarlo in Python, quindi l'ho impacchettato come pacchetto ffcount . Grazie per aver reso disponibile il codice @ChristopherSchultz!
GjjvdBurg

35

Hai provato a trovare? Per esempio:

find . -name "*.ext" | wc -l

1
Questo troverà ricorsivamente i file nella directory corrente.
mark4o,

Sul mio sistema, find /usr/share | wc -l(~ 137.000 file) è circa il 25% più veloce di ls -R /usr/share | wc -l(~ 160.000 righe inclusi nomi di directory , totali di directory e righe vuote) alla prima esecuzione di ciascuna e almeno due volte più veloce quando si confrontano le successive (cache).
In pausa fino a nuovo avviso.

11
Se vuole solo la directory corrente, non l'intero albero in modo ricorsivo, può aggiungere l'opzione -maxdepth 1 per trovare.
igustin,

3
Sembra che la ragione findsia più veloce di quanto non lssia a causa di come stai usando ls. Se si interrompe l'ordinamento lse si findottengono prestazioni simili.
Christopher Schultz,

17

find, ls e perl testati su 40.000 file: stessa velocità (anche se non ho provato a svuotare la cache):

[user@server logs]$ time find . | wc -l
42917

real    0m0.054s
user    0m0.018s
sys     0m0.040s
[user@server logs]$ time /bin/ls -f | wc -l
42918

real    0m0.059s
user    0m0.027s
sys     0m0.037s

e con perl opendir / readdir, contemporaneamente:

[user@server logs]$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"'
42918

real    0m0.057s
user    0m0.024s
sys     0m0.033s

nota: ho usato / bin / ls -f per essere sicuro di bypassare l'opzione alias che potrebbe rallentare un po 'e -f per evitare l'ordinamento dei file. ls senza -f è due volte più lento di find / perl, tranne se ls viene usato con -f, sembra essere lo stesso tempo:

[user@server logs]$ time /bin/ls . | wc -l
42916

real    0m0.109s
user    0m0.070s
sys     0m0.044s

Vorrei anche avere qualche script per chiedere direttamente al file system senza tutte le informazioni non necessarie.

test basati sulla risposta di Peter van der Heijden, glenn jackman e mark4o.

Tommaso


5
Dovresti assolutamente svuotare la cache tra i test. La prima volta che corro ls -l | wc -lsu una cartella su un HDD esterno da 2,5 "con file 1M, ci vogliono circa 3 minuti per completare l'operazione. La seconda volta ci vogliono 12 secondi IIRC. Anche questo potrebbe dipendere potenzialmente anche dal tuo file system. I stava usando Btrfs.
Behrang Saeedzadeh

Grazie, frammento di perl è la soluzione per me. $ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Pažout,

5

È possibile modificare l'output in base alle proprie esigenze, ma ecco una riga che ho scritto per contare ricorsivamente e riportare il numero di file in una serie di directory con nome numerico.

dir=/tmp/count_these/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$i => $(find ${dir}${i} -type f | wc -l),"; }

Questo cerca ricorsivamente tutti i file (non le directory) nella directory data e restituisce i risultati in un formato simile a un hash. Semplici modifiche al comando find potrebbero rendere più specifico il tipo di file che stai cercando di contare, ecc.

Risultati in qualcosa del genere:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,

1
Ho trovato l'esempio un po 'confuso. Mi chiedevo perché c'erano numeri a sinistra, anziché nomi di directory. Grazie per questo, però, ho finito per usarlo con alcune piccole modifiche. (contando le directory e rilasciando il nome della cartella di base. per i in $ (ls -1. | sort -n); {echo "$ i => $ (trova $ {i} | wc -l)";}
TheJacobTaylor

I numeri a sinistra sono i nomi delle mie directory dai miei dati di esempio. Mi dispiace che sia stato confuso.
Poteri

1
ls -1 ${dir}non funzionerà correttamente senza più spazi. Inoltre, non vi è alcuna garanzia che il nome restituito da lspossa essere passato find, poiché lssfugge ai caratteri non stampabili per il consumo umano. ( mkdir $'oddly\nnamed\ndirectory'se si desidera un caso di test particolarmente interessante). Vedi Perché non dovresti analizzare l'output di ls (1)
Charles Duffy,

4

Sorprendentemente per me, una scoperta a ossa nude è molto paragonabile a ls -f

> time ls -f my_dir | wc -l
17626

real    0m0.015s
user    0m0.011s
sys     0m0.009s

contro

> time find my_dir -maxdepth 1 | wc -l
17625

real    0m0.014s
user    0m0.008s
sys     0m0.010s

Ovviamente, i valori al terzo decimale si spostano un po 'ogni volta che si esegue uno di questi, quindi sono sostanzialmente identici. Si noti tuttavia che findrestituisce un'unità aggiuntiva, poiché conta la directory effettiva stessa (e, come accennato in precedenza, ls -frestituisce due unità extra, poiché conta anche. E ...).


4

Basta aggiungere questo per completezza. La risposta corretta ovviamente è già stata pubblicata da qualcun altro, ma puoi anche ottenere un conteggio di file e directory con il programma ad albero.

Esegui il comando tree | tail -n 1per ottenere l'ultima riga, che dirà qualcosa come "763 directory, 9290 file". Questo conta ricorsivamente file e cartelle, esclusi i file nascosti, che possono essere aggiunti con il flag -a. Per riferimento, ci sono voluti 4,8 secondi sul mio computer, per l'albero per contare tutta la mia home directory, che era 24777 directory, 238680 file. find -type f | wc -lci sono voluti 5,3 secondi, mezzo secondo in più, quindi penso che l'albero sia abbastanza competitivo in termini di velocità.

Finché non hai sottocartelle, tree è un modo semplice e veloce per contare i file.

Inoltre, e per puro divertimento, puoi usare tree | grep '^├'solo per mostrare i file / le cartelle nella directory corrente - questa è sostanzialmente una versione molto più lenta di ls.


Brew install tailper OS X.
The Unfun Cat

@TheUnfunCat taildovrebbe già essere installato sul tuo sistema Mac OS X.
Christopher Schultz,

4

Conteggio veloce dei file Linux

Il conteggio dei file Linux più veloce che conosco è

locate -c -r '/home'

Non v'è alcun bisogno di invocare grep! Ma come detto, dovresti avere un nuovo database (aggiornato quotidianamente da un lavoro cron o manuale da sudo updatedb).

Da uomo individuare

-c, --count
    Instead  of  writing  file  names on standard output, write the number of matching
    entries only.

Inoltre dovresti sapere che conta anche le directory come file!


A proposito: se vuoi una panoramica dei tuoi file e directory sul tuo tipo di sistema

locate -S

Emette il numero di directory, file ecc.


nota che devi assicurarti che il database sia aggiornato
phuclv,

1
LOL se hai già tutti i conteggi in un database, puoi sicuramente contare rapidamente. :)
Christopher Schultz,

3

Scrivere qui perché non ho abbastanza punti reputazione per commentare una risposta, ma mi è permesso lasciare la mia risposta, il che non ha senso. Comunque...

Per quanto riguarda la risposta di Christopher Schultz , suggerisco di cambiare stat in lstat e possibilmente di aggiungere un controllo dei limiti per evitare il buffer overflow:

if (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name) > PATH_MAX) {
    fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
    return;
}

Il suggerimento di usare lstat è di evitare di seguire i collegamenti simbolici che potrebbero portare a cicli se una directory contiene un collegamento simbolico a una directory padre.


2
Modificarlo perché l'uso di è lstatstato un buon suggerimento e ti meriti il ​​karma per questo. Questo suggerimento è stato incorporato nel mio codice pubblicato sopra e, ora, su GitHub.
Christopher Schultz,

2

Puoi provare se usare opendir()e readdir()in Perlè più veloce. Per un esempio di quelle funzioni, guarda qui


2
utilizzo: perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar (@files) '
glenn jackman,

2

Questa risposta qui è più veloce di quasi tutto il resto in questa pagina per directory molto grandi e molto annidate:

https://serverfault.com/a/691372/84703

locate -r '.' | grep -c "^$PWD"


1
Bello. Dato che hai già un db aggiornato di tutti i file, non è necessario riprovare. Ma sfortunatamente, è necessario assicurarsi che il comando updatedb sia già stato eseguito e completato per questo metodo.
Chris Reid,

non hai bisogno di grep. Usa locate -c -r '/path'come nella soluzione di
abu_bua

2

Sono venuto qui quando ho provato a contare i file in un set di dati di ~ 10K cartelle con ~ 10K file ciascuno. Il problema con molti degli approcci è che implicitamente stat i file 100M, il che richiede secoli.

Mi sono preso la libertà di estendere l'approccio di christopher-schultz in modo che supporti le directory di passaggio tramite args (anche il suo approccio ricorsivo usa stat).

Inserisci quanto segue nel file dircnt_args.c:

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dir;
    struct dirent *ent;
    long count;
    long countsum = 0;
    int i;

    for(i=1; i < argc; i++) {
        dir = opendir(argv[i]);
        count = 0;
        while((ent = readdir(dir)))
            ++count;

        closedir(dir);

        printf("%s contains %ld files\n", argv[i], count);
        countsum += count;
    }
    printf("sum: %ld\n", countsum);

    return 0;
}

Dopo un gcc -o dircnt_args dircnt_args.cpuoi invocarlo in questo modo:

dircnt_args /your/dirs/*

Su file da 100 M in cartelle da 10 K il precedente si completa abbastanza rapidamente (~ 5 minuti per la prima esecuzione, follow-up nella cache: ~ 23 s).

L'unico altro approccio che si è conclusa in meno di un'ora era ls con circa 1 min a cache: ls -f /your/dirs/* | wc -l. Il conteggio è disattivato da un paio di righe per dir ...

A parte il previsto, nessuno dei miei tentativi è findtornato entro un'ora: - /


Per qualcuno che non è un programmatore C, puoi spiegare perché questo sarebbe più veloce e come è in grado di ottenere la stessa risposta senza fare la stessa cosa?
mlissner

non è necessario essere un programmatore C, basta capire cosa significa stat un file e come sono rappresentate le directory: le directory sono essenzialmente elenchi di nomi di file e inode. Se si stat un file si accede all'inode che si trova da qualche parte sul disco per ottenere ad esempio informazioni come dimensione del file, autorizzazioni, .... Se sei solo interessato ai conteggi per directory non devi accedere alle informazioni sull'inode, il che potrebbe farti risparmiare un sacco di tempo.
Jörn Hees,

Questo segfaults su Oracle linux, gcc versione 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... i percorsi relativi e i fs remoti sembrano essere la causa
Rondo,

2

Il modo più veloce su Linux (la domanda è taggata come linux) è usare la chiamata di sistema diretta. Ecco un piccolo programma che conta i file (solo, nessuna directory) in una directory. Puoi contare milioni di file ed è circa 2,5 volte più veloce di "ls -f" e circa 1,3-1,5 volte più veloce della risposta di Christopher Schultz.

#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/syscall.h>

#define BUF_SIZE 4096

struct linux_dirent {
    long d_ino;
    off_t d_off;
    unsigned short d_reclen;
    char d_name[];
};

int countDir(char *dir) {


    int fd, nread, bpos, numFiles = 0;
    char d_type, buf[BUF_SIZE];
    struct linux_dirent *dirEntry;

    fd = open(dir, O_RDONLY | O_DIRECTORY);
    if (fd == -1) {
        puts("open directory error");
        exit(3);
    }
    while (1) {
        nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
        if (nread == -1) {
            puts("getdents error");
            exit(1);
        }
        if (nread == 0) {
            break;
        }

        for (bpos = 0; bpos < nread;) {
            dirEntry = (struct linux_dirent *) (buf + bpos);
            d_type = *(buf + bpos + dirEntry->d_reclen - 1);
            if (d_type == DT_REG) {
                // Increase counter
                numFiles++;
            }
            bpos += dirEntry->d_reclen;
        }
    }
    close(fd);

    return numFiles;
}

int main(int argc, char **argv) {

    if (argc != 2) {
        puts("Pass directory as parameter");
        return 2;
    }
    printf("Number of files in %s: %d\n", argv[1], countDir(argv[1]));
    return 0;
}

PS: Non è ricorsivo ma potresti modificarlo per farlo.


1
Non sono sicuro di essere d'accordo sul fatto che sia più veloce. Non ho rintracciato tutto ciò che il compilatore fa con opendir/ readdir, ma sospetto che alla fine si riduca quasi allo stesso codice. Anche effettuare chiamate di sistema in questo modo non è portatile e, poiché l'ABI Linux non è stabile, non è garantito che un programma compilato su un sistema funzioni correttamente su un altro (sebbene sia un buon consiglio di compilare qualsiasi cosa dalla sorgente su qualsiasi sistema * NIX IMO ). Se la velocità è la chiave, questa è una buona soluzione se effettivamente migliora la velocità - non ho confrontato i programmi separatamente.
Christopher Schultz,

1

lspassa più tempo a ordinare i nomi dei file, usando -fper disabilitare l'ordinamento si risparmia qualche volta:

ls -f | wc -l

oppure puoi usare find:

find . -type f | wc -l

0

Mi sono reso conto che non utilizzare nell'elaborazione della memoria quando si dispone di un'enorme quantità di dati è più veloce di "eseguire il piping" dei comandi. Quindi ho salvato il risultato in un file e dopo averlo analizzato

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l

questa non è la soluzione più veloce perché i dischi rigidi sono estremamente lenti. Ci sono altri modi più efficienti che sono stati pubblicati anni prima di te
phuclv,

0

Dovresti usare "getdents" al posto di ls / find

Ecco un ottimo articolo che descrive l'approccio getdents.

http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html

Ecco l'estratto:

E praticamente ogni altro metodo per elencare una directory (incluso python os.listdir, find.) si basa su libc readdir (). Comunque readdir () legge solo 32K di voci di directory alla volta, il che significa che se hai molti file nella stessa directory (cioè 500M di voci di directory), ci vorrà un tempo follemente lungo per leggere tutte le voci di directory , specialmente su un disco lento. Per le directory che contengono un gran numero di file, dovrai scavare più a fondo degli strumenti che si basano su readdir (). Dovrai usare direttamente syscall getdents (), piuttosto che i metodi helper di libc.

Possiamo trovare il codice C per elencare i file usando getdents () da qui :

Sono necessarie due modifiche per elencare rapidamente tutti i file in una directory.

Innanzitutto, aumenta la dimensione del buffer da X a qualcosa come 5 megabyte.

#define BUF_SIZE 1024*1024*5

Quindi modificare il ciclo principale in cui stampa le informazioni su ciascun file nella directory per saltare le voci con inode == 0. L'ho fatto aggiungendo

if (dp->d_ino != 0) printf(...);

Nel mio caso mi sono anche preoccupato solo dei nomi dei file nella directory, quindi ho anche riscritto l'istruzione printf () per stampare solo il nome del file.

if(d->d_ino) printf("%sn ", (char *) d->d_name);

Compilalo (non ha bisogno di alcuna libreria esterna, quindi è super semplice da fare)

gcc listdir.c -o listdir

Adesso corri e basta

./listdir [directory with insane number of files]

Nota che Linux fa un read-ahead, quindi in readdir()realtà non è lento. Ho bisogno di una figura solida prima di credere che valga la pena buttare via la portabilità per questo guadagno in termini di prestazioni.
fuz,

-1

Preferisco il seguente comando per tenere traccia delle modifiche al numero di file in una directory.

watch -d -n 0.01 'ls | wc -l'

Il comando manterrà una finestra aperta per tenere traccia del numero di file presenti nella directory con una frequenza di aggiornamento di 0,1 sec.


sei sicuro che ls | wc -lfinirà per una cartella con migliaia o milioni di file in 0,01s? anche il tuo lsè estremamente inefficiente rispetto ad altre soluzioni. E l'OP vuole solo ottenere il conteggio, non stare seduto lì a guardare l'output che cambia
phuclv,

Bene. Bene. Ho trovato una soluzione elegante che funziona per me. Vorrei condividere lo stesso, quindi ho fatto. Non so che il comando 'ls' in Linux sia altamente inefficiente. Cosa stai usando invece di quello? E 0,01s è la frequenza di aggiornamento. Non è il momento. se non hai usato l'orologio, consulta le pagine man.
Anoop Toffy,

bene ho letto il watchmanuale dopo quel commento e ho visto che 0,01s (non 0,1s) è un numero non realistico perché la frequenza di aggiornamento della maggior parte degli schermi di PC è solo di 60Hz, e questo non risponde in alcun modo alla domanda. L'OP ha chiesto "Conteggio veloce dei file Linux per un gran numero di file". Inoltre non hai letto le risposte disponibili prima di pubblicare
phuclv,

Ho letto le risposte. Ma quello che ho pubblicato è un modo per tenere traccia del cambiamento del numero di file in una directory. ad esempio: durante la copia di file da una posizione a un'altra il numero di file mantiene le modifiche. con il metodo I poster si può tenerne traccia. Accetto che il post che ho pubblicato non sia stato modificato o migliorato nei post precedenti.
Anoop Toffy,

-2

Primi 10 registi con il numero più alto di file.

dir=/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$(find ${dir}${i} \
    -type f | wc -l) => $i,"; } | sort -nr | head -10

3
Questo sembra sorprendentemente simile alla risposta (con gli stessi bug) scritta dai potenti . Se hai intenzione di estendere o modificare il codice scritto da qualcun altro, l'accreditamento è appropriato. Comprendere il codice che stai utilizzando nelle tue risposte abbastanza per identificare e correggere i suoi bug è ancora più appropriato.
Charles Duffy,
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.