Szybkie liczenie plików w systemie Linux dla dużej liczby plików

136

Próbuję znaleźć najlepszy sposób na znalezienie liczby plików w określonym katalogu, gdy jest ich bardzo dużo (> 100 000).

Gdy jest tak wiele plików, wykonanie operacji ls | wc -lzajmuje dużo czasu. Myślę, że dzieje się tak, ponieważ zwraca nazwy wszystkich plików. Próbuję zająć jak najmniej IO dysku.

Bezskutecznie eksperymentowałem z niektórymi skryptami powłoki i Perla. Jakieś pomysły?

ks1322
źródło
2
upewnij się, że twoje "ls" to / usr / bin / ls, a nie alias do czegoś bardziej wyszukanego.
glenn jackman
Podobne pytanie z interesującymi odpowiedziami tutaj: serverfault.com/questions/205071/ ...
aidan
Warto zauważyć, że większość, jeśli nie wszystkie, rozwiązania przedstawione w tym pytaniu nie są specyficzne dla Linuksa , ale są dość ogólne dla wszystkich systemów typu * NIX. Być może usunięcie tagu „Linux” jest właściwe.
Christopher Schultz

Odpowiedzi:

188

Domyślnie lssortuje nazwy, co może chwilę potrwać, jeśli jest ich dużo. Nie będzie również wyjścia, dopóki wszystkie nazwy nie zostaną odczytane i posortowane. Użyj ls -fopcji, aby wyłączyć sortowanie.

ls -f | wc -l

Należy pamiętać, że będzie to również włączyć -a, tak ., ..i inne pliki zaczynające się .będą zliczane.

mark4o
źródło
11
+1 I myślałem, że wiem wszystko, o czym trzeba wiedzieć ls.
mob
5
ZOMG. Sortowanie 100K wierszy to nic - w porównaniu do stat()wywołania lskażdego pliku. findnie, stat()więc działa szybciej.
Dummy00001
12
ls -fteż nie stat(). Ale oczywiście oba lsi finddzwonią, stat()gdy używane są pewne opcje, takie jak ls -llub find -mtime.
mark4o
7
Z punktu widzenia kontekstu, naliczenie 2,5 miliona plików jpg na małym pudełku Slicehost zajęło 1-2 minuty.
philfreo
6
Jeśli chcesz dodać podkatalogi do liczby, zróbls -fR | wc -l
Ryan Walls
62

Najszybszym sposobem jest specjalny program, taki jak ten:

#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;
}

Z moich testów bez względu na pamięć podręczną, uruchomiłem każdy z nich około 50 razy w tym samym katalogu, w kółko, aby uniknąć wypaczenia danych w pamięci podręcznej, i otrzymałem z grubsza następujące dane dotyczące wydajności (w czasie rzeczywistym):

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

Ten ostatni dircntto program skompilowany z powyższego źródła.

EDYCJA 2016-09-26

Ze względu na powszechne zapotrzebowanie ponownie napisałem ten program tak, aby był rekurencyjny, więc będzie spadał do podkatalogów i nadal będzie oddzielnie liczył pliki i katalogi.

Ponieważ jest jasne, że niektórzy ludzie chcą wiedzieć, jak to wszystko zrobić, mam wiele komentarzy w kodzie, aby spróbować pokazać, co się dzieje. Napisałem to i przetestowałem na 64-bitowym Linuksie, ale powinno działać na każdym systemie zgodnym z POSIX, w tym Microsoft Windows. Raporty o błędach są mile widziane; Z przyjemnością zaktualizuję to, jeśli nie możesz go uruchomić w systemie AIX, OS / 400 lub czymkolwiek.

Jak widać, jest to o wiele bardziej skomplikowane niż oryginał i koniecznie tak: przynajmniej jedna funkcja musi istnieć, aby była wywoływana rekurencyjnie, chyba że chcesz, aby kod stał się bardzo złożony (np. Zarządzanie stosem podkatalogów i przetwarzanie go w pojedynczej pętli). Ponieważ musimy sprawdzać typy plików, w grę wchodzą różnice między różnymi systemami operacyjnymi, standardowymi bibliotekami itp., Dlatego napisałem program, który stara się być użyteczny w każdym systemie, w którym będzie się kompilował.

Jest bardzo mało sprawdzania błędów, a countsama funkcja tak naprawdę nie raportuje błędów. Jedyne wywołania, które naprawdę mogą zawieść, to opendiri stat(jeśli nie masz szczęścia i masz system, w którym już direntzawiera typ pliku). Nie mam paranoi na punkcie sprawdzania całkowitej długości nazw ścieżek podkatalogu, ale teoretycznie system nie powinien zezwalać na żadną nazwę ścieżki dłuższą niż PATH_MAX. Jeśli są jakieś obawy, mogę to naprawić, ale jest to po prostu więcej kodu, które trzeba wyjaśnić osobie uczącej się pisać C. Ten program ma być przykładem tego, jak rekursywnie zagłębiać się w podkatalogi.

#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;
}

EDYCJA 2017-01-17

Wprowadziłem dwie zmiany sugerowane przez @FlyingCodeMonkey:

  1. Użyj lstatzamiast stat. Zmieni to zachowanie programu, jeśli w skanowanym katalogu znajdują się dowiązane symbolicznie katalogi. Poprzednie zachowanie polegało na tym, że do (połączonego) podkatalogu dodawano liczbę plików do ogólnej liczby; nowe zachowanie polega na tym, że połączony katalog będzie liczony jako pojedynczy plik, a jego zawartość nie będzie liczona.
  2. Jeśli ścieżka do pliku jest zbyt długa, zostanie wyemitowany komunikat o błędzie i program zatrzyma się.

EDYCJA 2017-06-29

Przy odrobinie szczęścia będzie to ostatnia edycja tej odpowiedzi :)

Skopiowałem ten kod do repozytorium GitHub, aby nieco łatwiej było uzyskać kod (zamiast kopiować / wklejać, możesz po prostu pobrać źródło ), a ponadto ułatwia każdemu zaproponowanie modyfikacji poprzez przesłanie pull -request z GitHub.

Źródło jest dostępne na licencji Apache License 2.0. Łatki * mile widziane!


  • „patch” jest tym, co starzy ludzie tacy jak ja nazywają „pull request”.
Christopher Schultz
źródło
2
Po prostu świetnie! dzięki! A dla nieświadomych: możesz wypełnić powyższy kod w terminalu: gcc -o dircnt dircnt.ci użyj tak./dircnt some_dir
aesede
Czy istnieje łatwy sposób, aby uczynić to rekursywnym?
ck_
@ck_ Jasne, można to łatwo przekształcić w rekurencję. Potrzebujesz pomocy z rozwiązaniem, czy chcesz, żebym napisała całość?
Christopher Schultz
1
@ChristopherSchultz, testy porównawcze, które opublikowałeś powyżej - jak duży był ten katalog?
Dom Vinyard
1
Naprawdę chciałem użyć tego w Pythonie, więc spakowałem go jako pakiet ffcount . Dziękujemy za udostępnienie kodu @ChristopherSchultz!
GjjvdBurg
35

Czy próbowałeś znaleźć? Na przykład:

find . -name "*.ext" | wc -l
igustin
źródło
1
Spowoduje to rekurencyjne znalezienie plików w bieżącym katalogu.
mark4o
W moim systemie find /usr/share | wc -l(~ 137 000 plików) jest około 25% szybsze niż ls -R /usr/share | wc -l(~ 160 000 wierszy, w tym nazwy katalogów , sumy katalogów i puste wiersze) przy pierwszym uruchomieniu każdego z nich i co najmniej dwa razy szybsze podczas porównywania kolejnych (buforowanych) uruchomień.
Wstrzymano do odwołania.
11
Jeśli chce tylko bieżący katalog, a nie rekurencyjnie całe drzewo, może dodać opcję -maxdepth 1, aby znaleźć.
igustin
3
Wydaje się, że przyczyna findjest szybsza niż z lspowodu tego, jak używasz ls. Jeśli przerwiesz sortowanie lsi uzyskasz findpodobną wydajność.
Christopher Schultz
17

find, ls i perl przetestowano na 40 000 plików: ta sama prędkość (chociaż nie próbowałem wyczyścić pamięci podręcznej):

[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

i z perl opendir / readdir, w tym samym czasie:

[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

uwaga: użyłem / bin / ls -f, aby upewnić się, że omijam opcję aliasu, która może trochę spowolnić, oraz -f, aby uniknąć porządkowania plików. ls bez -f jest dwukrotnie wolniejsze niż find / perl, z wyjątkiem tego, że jeśli ls jest używane z -f, wydaje się, że jest to ten sam czas:

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

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

Chciałbym również mieć skrypt, który bezpośrednio zadaje system plików bez wszystkich niepotrzebnych informacji.

testy oparte na odpowiedzi Petera van der Heijdena, Glenna Jackmana i mark4o.

Tomasz

Tomasz
źródło
5
Zdecydowanie powinieneś wyczyścić pamięć podręczną między testami. Gdy po raz pierwszy uruchamiam ls -l | wc -lna folderze na zewnętrznym dysku twardym 2,5 "z 1 MB plików, operacja trwa około 3 minut. Za drugim razem zajmuje to 12 sekund IIRC. Może to również potencjalnie zależeć od systemu plików. I używał Btrfs.
Behrang Saeedzadeh
Dziękuję, Perl Snippet jest dla mnie rozwiązaniem. $ 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

Możesz zmienić dane wyjściowe w zależności od swoich wymagań, ale oto jeden wiersz bash, który napisałem, aby rekurencyjnie zliczać i raportować liczbę plików w serii katalogów nazwanych numerycznie.

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

To wyszukuje rekurencyjnie wszystkie pliki (nie katalogi) w podanym katalogu i zwraca wyniki w formacie podobnym do skrótu. Proste poprawki w poleceniu wyszukiwania mogą sprawić, że typy plików, które chcesz policzyć, będą bardziej szczegółowe itp.

Daje coś takiego:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,
mightybs
źródło
1
Uważam, że przykład jest trochę zagmatwany. Zastanawiałem się, dlaczego po lewej stronie są numery zamiast nazw katalogów. Dziękuję za to, skończyło się na tym, że użyłem go z kilkoma drobnymi poprawkami. (licząc katalogi i porzucając nazwę folderu podstawowego. for i in $ (ls -1. | sort -n); {echo "$ i => $ (find $ {i} | wc -l)";}
TheJacobTaylor
Numery po lewej stronie to nazwy moich katalogów z moich przykładowych danych. Przepraszam, to było zagmatwane.
mightybs
1
ls -1 ${dir}nie będzie działać poprawnie bez większej ilości spacji. Ponadto nie ma gwarancji, że nazwa zwrócona przez lsmoże zostać przekazana find, ponieważ lsnie zawiera znaków niedrukowalnych do spożycia przez ludzi. ( mkdir $'oddly\nnamed\ndirectory'jeśli chcesz szczególnie interesującego przypadku testowego). Zobacz Dlaczego nie powinieneś analizować wyniku ls (1)
Charles Duffy,
4

Zaskakująco dla mnie znalezisko gołej kości jest bardzo podobne do ls -f

> time ls -f my_dir | wc -l
17626

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

przeciw

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

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

Oczywiście wartości na trzecim miejscu po przecinku przesuwają się nieco za każdym razem, gdy wykonujesz którekolwiek z nich, więc są w zasadzie identyczne. Zwróć jednak uwagę, że findzwraca jedną dodatkową jednostkę, ponieważ zlicza sam katalog (i, jak wspomniano wcześniej, ls -fzwraca dwie dodatkowe jednostki, ponieważ liczy również… i…).

Bogdan Stăimentscu
źródło
4

Dodam to tylko ze względu na kompletność. Prawidłowa odpowiedź została oczywiście wysłana przez kogoś innego, ale możesz również uzyskać liczbę plików i katalogów za pomocą programu drzewiastego.

Uruchom polecenie, tree | tail -n 1aby uzyskać ostatnią linię, która powie coś w rodzaju „763 katalogów, 9290 plików”. Zlicza pliki i foldery rekurencyjnie, z wyłączeniem plików ukrytych, które można dodać za pomocą flagi -a. Dla porównania, na moim komputerze zajęło mi to 4,8 sekundy, zanim drzewo policzyło cały mój katalog domowy, który zawierał 24777 katalogów, 238680 plików. find -type f | wc -lzajęło to 5,3 sekundy, pół sekundy dłużej, więc myślę, że drzewo jest dość konkurencyjne pod względem szybkości.

Dopóki nie masz żadnych podfolderów, drzewo jest szybkim i łatwym sposobem zliczania plików.

Ponadto, dla samej przyjemności, możesz używać tree | grep '^├'tylko do wyświetlania plików / folderów w bieżącym katalogu - jest to w zasadzie znacznie wolniejsza wersja ls.

Benubird
źródło
Brew install taildla OS X.
The Unfun Cat
@TheUnfunCat tailpowinien być już zainstalowany w systemie Mac OS X.
Christopher Schultz
4

Szybka liczba plików w systemie Linux

Najszybsza liczba plików linux, jaką znam, to

locate -c -r '/home'

Nie ma potrzeby wywoływania grep! Ale jak wspomniano, powinieneś mieć świeżą bazę danych (aktualizowaną codziennie przez zadanie cron lub ręcznie przez sudo updatedb).

Od człowieka zlokalizuj

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

Dodatkowo powinieneś wiedzieć, że liczy również katalogi jako pliki!


BTW: Jeśli chcesz mieć przegląd plików i katalogów w Twoim systemie

locate -S

Wyświetla liczbę katalogów, plików itp.

abu_bua
źródło
pamiętaj, że musisz się upewnić, że baza danych jest aktualna
phuclv
1
LOL, jeśli masz już wszystkie liczby w bazie danych, z pewnością możesz szybko policzyć. :)
Christopher Schultz
3

Pisząc to tutaj, ponieważ nie mam wystarczającej liczby punktów reputacji, aby skomentować odpowiedź, ale mogę zostawić własną odpowiedź, co nie ma sensu. Tak czy siak...

Jeśli chodzi o odpowiedź Christophera Schultza , proponuję zmienić stat na lstat i ewentualnie dodać sprawdzanie granic, aby uniknąć przepełnienia bufora:

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;
}

Sugestią użycia lstat jest unikanie podążania za dowiązaniami symbolicznymi, które mogłyby prowadzić do cykli, jeśli katalog zawiera dowiązanie symboliczne do katalogu nadrzędnego.

FlyingCodeMonkey
źródło
2
Modyfikowanie, ponieważ użycie lstatbyło dobrą sugestią i zasługujesz na karmę za to. Ta sugestia została włączona do mojego kodu zamieszczonego powyżej, a teraz na GitHubie.
Christopher Schultz
2

Można spróbować, jeśli korzystają opendir()i readdir()w Perljest szybsze. Przykład tych funkcji znajdziesz tutaj

Peter van der Heijden
źródło
2
użycie: perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar (@files) ''
glenn jackman
2

Ta odpowiedź jest szybsza niż prawie wszystko inne na tej stronie w przypadku bardzo dużych, bardzo zagnieżdżonych katalogów:

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

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

ck_
źródło
1
Miły. Ponieważ masz już aktualną bazę danych wszystkich plików, nie musisz już do tego wracać. Niestety, musisz się upewnić, że polecenie updatedb zostało już uruchomione i zakończone dla tej metody.
Chris Reid,
nie musisz grep. Użyj locate -c -r '/path'jak w roztworze
abu_bua
2

Przyszedłem tutaj, próbując policzyć pliki w zestawie danych zawierającym ~ 10K folderów po ~ 10K plików każdy. Problem z wieloma podejściami polega na tym, że niejawnie zapisują pliki 100M, co zajmuje wieki.

Pozwoliłem sobie rozszerzyć to podejście o Christophera-Schultza, tak aby obsługiwało przekazywanie katalogów przez args (jego podejście rekurencyjne również używa stat).

Umieść w pliku 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;
}

Po a gcc -o dircnt_args dircnt_args.cmożesz go wywołać w ten sposób:

dircnt_args /your/dirs/*

W przypadku plików 100M w folderach 10K powyższe czynności kończy się dość szybko (~ 5 minut przy pierwszym uruchomieniu, kontynuacja w pamięci podręcznej: ~ 23 s).

Tylko inne podejście, które ukończył w czasie krótszym niż godzina była ls z około 1 min na cache: ls -f /your/dirs/* | wc -l. Liczenie jest jednak zmniejszone o kilka nowych linii na katalog ...

Poza oczekiwaniami, żadna z moich prób find zwróciła się w ciągu godziny: - /

Jörn Hees
źródło
Czy dla kogoś, kto nie jest programistą C, możesz wyjaśnić, dlaczego byłoby to szybsze i jak można uzyskać tę samą odpowiedź bez robienia tego samego?
mlissner
nie musisz być programistą C, po prostu zrozum, co to znaczy statować plik i jak reprezentowane są katalogi: katalogi to zasadniczo listy nazw plików i i-węzłów. Jeśli wyślesz plik, uzyskasz dostęp do i-węzła, który jest gdzieś na dysku, aby na przykład uzyskać informacje takie jak rozmiar pliku, uprawnienia, .... Jeśli interesują Cię tylko liczby na katalog, nie musisz uzyskiwać dostępu do informacji o i-węzłach, co może zaoszczędzić dużo czasu.
Jörn Hees
To segfaults w Oracle linux, gcc wersja 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... ścieżki względne i zdalne fs wydają się być przyczyną
Rondo
2

Najszybszym sposobem na Linuksie (pytanie jest oznaczone jako linux), jest użycie bezpośredniego wywołania systemowego. Oto mały program, który liczy pliki (tylko, bez katalogów) w katalogu. Możesz policzyć miliony plików i jest to około 2,5 razy szybsze niż „ls -f” i około 1,3–1,5 razy szybsze niż odpowiedź Christophera Schultza.

#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: Nie jest rekurencyjny, ale możesz go zmodyfikować, aby to osiągnąć.

Nikolay Dimitrov
źródło
1
Nie jestem pewien, zgadzam się, że to jest szybsze. Nie prześledziłem wszystkiego, co kompilator robi z opendir/ readdir, ale podejrzewam, że na końcu sprowadza się to do prawie tego samego kodu. Wykonywanie wywołań systemowych w ten sposób również nie jest przenośne, a ponieważ ABI Linuksa nie jest stabilne, program skompilowany na jednym systemie nie gwarantuje prawidłowego działania na innym (chociaż dość dobrą radą jest kompilowanie czegokolwiek ze źródła na dowolnym systemie IMO * NIX ). Jeśli szybkość jest kluczowa, jest to dobre rozwiązanie, jeśli faktycznie poprawia szybkość - nie testowałem programów oddzielnie.
Christopher Schultz
1

lspoświęca więcej czasu na sortowanie nazw plików, użycie -fdo wyłączenia sortowania pozwoli czasem zaoszczędzić:

ls -f | wc -l

lub możesz użyć find:

find . -type f | wc -l
Mohammad Anini
źródło
0

Zdałem sobie sprawę, że nieużywanie w przetwarzaniu pamięci, gdy masz ogromną ilość danych, jest szybsze niż „potokowanie” poleceń. Zapisałem więc wynik do pliku i po przeanalizowaniu

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l
Marcelo Luiz Onhate
źródło
nie jest to najszybsze rozwiązanie, ponieważ dyski twarde działają bardzo wolno. Istnieją inne bardziej wydajne sposoby, które zostały opublikowane lata wcześniej
phuclv
0

Powinieneś użyć "getdents" zamiast ls / find

Oto jeden bardzo dobry artykuł, który opisuje podejście getdents.

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

Oto wyciąg:

ls i praktycznie każda inna metoda wyświetlania katalogu (w tym python os.listdir, find.) opierają się na libc readdir (). Jednak readdir () odczytuje tylko 32 KB wpisów w katalogu na raz, co oznacza, że ​​jeśli masz dużo plików w tym samym katalogu (tj. 500 M wpisów katalogu), odczytanie wszystkich pozycji katalogu zajmie niesamowicie dużo czasu , zwłaszcza na wolnym dysku. W przypadku katalogów zawierających dużą liczbę plików musisz sięgnąć głębiej niż narzędzia, które opierają się na readdir (). Będziesz musiał użyć bezpośrednio metody getdents () syscall, zamiast metod pomocniczych z libc.

Możemy znaleźć kod C do wyświetlenia listy plików za pomocą getdents () stąd :

Aby szybko wyświetlić listę wszystkich plików w katalogu, musisz wykonać dwie modyfikacje.

Najpierw zwiększ rozmiar bufora z X do około 5 megabajtów.

#define BUF_SIZE 1024*1024*5

Następnie zmodyfikuj główną pętlę, w której wypisuje informacje o każdym pliku w katalogu, aby pominąć wpisy z inode == 0. Zrobiłem to przez dodanie

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

W moim przypadku tak naprawdę zależało mi tylko na nazwach plików w katalogu, więc przepisałem również instrukcję printf (), aby wydrukować tylko nazwę pliku.

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

Skompiluj (nie potrzebuje żadnych zewnętrznych bibliotek, więc jest to bardzo proste)

gcc listdir.c -o listdir

Teraz po prostu biegnij

./listdir [directory with insane number of files]
Dev123
źródło
Zauważ, że Linux wykonuje odczyt z wyprzedzeniem, więc w readdir()rzeczywistości nie jest wolny. Potrzebuję solidnej sylwetki, zanim uznam, że warto odrzucić przenośność, aby uzyskać ten wzrost wydajności.
fuz
-1

Wolę następujące polecenie, aby śledzić zmiany w liczbie plików w katalogu.

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

Polecenie pozostawi otwarte okno, aby śledzić liczbę plików znajdujących się w katalogu z częstotliwością odświeżania 0,1 sekundy.

Anoop Toffy
źródło
Czy na pewno ls | wc -lzakończy się to dla folderu z tysiącami lub milionami plików w 0,01 s? nawet twój lsjest niezwykle nieefektywny w porównaniu z innymi rozwiązaniami. A OP chce tylko policzyć, nie siedząc tam i patrząc na zmianę wyjścia
phuclv
Dobrze. Dobrze. Znalazłem eleganckie rozwiązanie, które mi odpowiada. Chciałbym podzielić się tym samym, dlatego to zrobiłem. Nie wiem, czy polecenie „ls” w systemie Linux jest wysoce nieefektywne. Czego zamiast tego używasz? A 0,01s to częstotliwość odświeżania. Nie czas. jeśli nie korzystałeś z watch, zajrzyj na strony man.
Anoop Toffy
cóż, przeczytałem watchinstrukcję po tym komentarzu i zobaczyłem, że 0,01 s (a nie 0,1 s) to liczba nierealna, ponieważ częstotliwość odświeżania większości ekranów komputerów osobistych wynosi tylko 60 Hz, a to w żaden sposób nie odpowiada na pytanie. OP zapytał o „Szybką liczbę plików Linuksa dla dużej liczby plików”. Nie przeczytałeś również żadnych dostępnych odpowiedzi przed wysłaniem
phuclv
Przeczytałem odpowiedzi. Ale to, co opublikowałem, to sposób na śledzenie zmiany liczby plików w katalogu. na przykład: podczas kopiowania pliku z jednej lokalizacji do drugiej liczba plików zachowuje zmiany. dzięki metodzie, którą zamieszczam, można to śledzić. Zgadzam się, że post, który stworzyłem, nie zmienił ani nie poprawił żadnych poprzednich postów.
Anoop Toffy,
-2

Pierwszych 10 reżyserów z największą liczbą plików.

dir=/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$(find ${dir}${i} \
    -type f | wc -l) => $i,"; } | sort -nr | head -10
user2546874
źródło
3
To z pewnością wygląda zadziwiająco podobnie do odpowiedzi (z tymi samymi błędami) napisanej przez mightybs . Jeśli zamierzasz rozszerzyć lub zmodyfikować kod napisany przez kogoś innego, uznanie go jest właściwe. Zrozumienie kodu, którego używasz w swoich odpowiedziach na tyle, aby zidentyfikować i naprawić jego błędy, jest jeszcze bardziej odpowiednie.
Charles Duffy,