Hitungan File Linux Cepat untuk sejumlah besar file

137

Saya mencoba mencari cara terbaik untuk menemukan jumlah file di direktori tertentu ketika ada jumlah file yang sangat besar (> 100.000).

Ketika ada banyak file, melakukan ls | wc -lmembutuhkan waktu yang cukup lama untuk dieksekusi. Saya percaya ini karena mengembalikan nama semua file. Saya mencoba mengambil IO disk sesedikit mungkin.

Saya telah bereksperimen dengan beberapa shell dan skrip Perl tidak berhasil. Ada ide?

ks1322
sumber
2
pastikan bahwa "ls" Anda adalah / usr / bin / ls dan bukan alias untuk sesuatu yang lebih menarik.
glenn jackman
Pertanyaan serupa dengan jawaban menarik di sini: serverfault.com/questions/205071/…
aidan
Nilainya menunjukkan bahwa sebagian besar, jika tidak semua solusi yang disajikan untuk pertanyaan ini tidak spesifik untuk Linux , tetapi cukup umum untuk semua sistem * mirip-NIX. Mungkin menghapus tag "Linux" sesuai.
Christopher Schultz

Jawaban:

189

Secara default lsmengurutkan nama, yang bisa memakan waktu cukup lama jika ada banyak dari mereka. Juga tidak akan ada output sampai semua nama dibaca dan disortir. Gunakan ls -fopsi untuk mematikan penyortiran.

ls -f | wc -l

Catatan bahwa ini juga akan memungkinkan -a, jadi ., ..file, dan lain dimulai dengan .akan dihitung.

mark4o
sumber
11
+1 Dan saya pikir saya tahu semua yang perlu diketahui ls.
mob
5
ZOMG. Menyortir 100 ribu baris tidak ada artinya - dibandingkan dengan stat()panggilan yang lsdilakukan pada setiap file. findtidak stat()dengan demikian bekerja lebih cepat.
Dummy00001
12
ls -fjuga tidak stat(). Tapi tentu saja keduanya lsdan findmenelepon stat()ketika opsi tertentu digunakan, seperti ls -latau find -mtime.
mark4o
7
Untuk konteks, ini membutuhkan 1-2 menit untuk menghitung 2,5 juta jpg pada kotak Slicehost kecil.
philfreo
6
Jika Anda ingin menambahkan subdirektori ke hitungan, lakukanls -fR | wc -l
Ryan Walls
62

Cara tercepat adalah program yang dibuat khusus, seperti ini:

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

Dari pengujian saya tanpa memperhatikan cache, saya menjalankan masing-masing sekitar 50 kali masing-masing terhadap direktori yang sama, berulang-ulang, untuk menghindari kemiringan data berbasis cache, dan saya mendapat kira-kira angka kinerja berikut (dalam waktu jam nyata):

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

Yang terakhir dircnt,, adalah program yang disusun dari sumber di atas.

EDIT 2016-09-26

Karena banyaknya permintaan, saya menulis ulang program ini agar bersifat rekursif, sehingga akan masuk ke subdirektori dan terus menghitung file dan direktori secara terpisah.

Karena jelas beberapa orang ingin tahu bagaimana melakukan semua ini, saya punya banyak komentar dalam kode untuk mencoba memperjelas apa yang terjadi. Saya menulis ini dan mengujinya di Linux 64-bit, tetapi seharusnya bekerja pada sistem yang mendukung POSIX, termasuk Microsoft Windows. Laporan bug diterima; Saya senang memperbarui ini jika Anda tidak bisa membuatnya bekerja pada AIX atau OS / 400 Anda atau apa pun.

Seperti yang Anda lihat, ini jauh lebih rumit daripada yang asli dan tentu saja demikian: setidaknya satu fungsi harus ada untuk dipanggil secara rekursif kecuali jika Anda ingin kode menjadi sangat kompleks (misalnya mengelola tumpukan subdirektori dan memprosesnya dalam satu loop). Karena kita harus memeriksa jenis file, perbedaan antara OS yang berbeda, perpustakaan standar, dll ikut bermain, jadi saya telah menulis sebuah program yang mencoba untuk dapat digunakan pada sistem mana pun yang akan dikompilasi.

Ada sangat sedikit pengecekan kesalahan, dan countfungsinya sendiri tidak benar-benar melaporkan kesalahan. Satu-satunya panggilan yang benar-benar dapat gagal adalah opendirdan stat(jika Anda tidak beruntung dan memiliki sistem di mana sudah direntberisi jenis file). Saya tidak paranoid tentang memeriksa panjang total nama path subdir, tetapi secara teoritis, sistem seharusnya tidak mengizinkan nama path yang lebih panjang dari pada PATH_MAX. Jika ada masalah, saya dapat memperbaikinya, tetapi hanya kode yang perlu dijelaskan kepada seseorang yang sedang belajar menulis C. Program ini dimaksudkan untuk menjadi contoh tentang bagaimana menyelami subdirektori secara rekursif.

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

EDIT 2017-01-17

Saya telah memasukkan dua perubahan yang disarankan oleh @FlyingCodeMonkey:

  1. Gunakan lstatsebagai ganti stat. Ini akan mengubah perilaku program jika Anda memiliki direktori yang disinkronkan di direktori yang Anda pindai. Perilaku sebelumnya adalah bahwa subdirektori (ditautkan) akan memiliki jumlah file ditambahkan ke jumlah keseluruhan; perilaku baru adalah bahwa direktori yang ditautkan akan dihitung sebagai satu file, dan isinya tidak akan dihitung.
  2. Jika jalur file terlalu panjang, pesan kesalahan akan dipancarkan dan program akan berhenti.

EDIT 2017-06-29

Dengan sedikit keberuntungan, ini akan menjadi edit terakhir dari jawaban ini :)

Saya telah menyalin kode ini ke dalam repositori GitHub untuk membuatnya sedikit lebih mudah untuk mendapatkan kode (alih-alih menyalin / menempel, Anda dapat mengunduh sumbernya ), ditambah lagi membuatnya lebih mudah bagi siapa saja untuk menyarankan modifikasi dengan mengirimkan tarikan -meminta bantuan dari GitHub.

Sumber tersedia di bawah Apache License 2.0. Tambalan * selamat datang!


  • "tambalan" adalah apa yang orang tua seperti saya sebut "permintaan tarik".
Christopher Schultz
sumber
2
Luar biasa! Terima kasih! Dan bagi mereka yang tidak sadar: Anda dapat mengkompilasi kode di atas di terminal: gcc -o dircnt dircnt.cdan gunakan seperti ini./dircnt some_dir
aesede
Apakah ada cara mudah untuk membuat ini rekursif?
ck_
@ck_ Tentu, ini bisa dengan mudah dibuat rekursif. Apakah Anda memerlukan bantuan dengan solusinya, atau Anda ingin saya menulis semuanya?
Christopher Schultz
1
@ChristopherSchultz, tolok ukur yang Anda posting di atas - seberapa besar direktori yang dimaksud?
Dom Vinyard
1
Saya benar-benar ingin menggunakan ini di Python jadi saya mengemasnya sebagai paket ffcount . Terima kasih telah menyediakan kode @ChristopherSchultz!
GjjvdBurg
35

Apakah Anda mencoba mencari? Sebagai contoh:

find . -name "*.ext" | wc -l
igustin
sumber
1
Ini akan secara rekursif menemukan file di bawah direktori saat ini.
mark4o
Pada sistem saya, find /usr/share | wc -l(~ 137.000 file) sekitar 25% lebih cepat daripada ls -R /usr/share | wc -l(~ 160.000 baris termasuk nama dir, total dir dan baris kosong) pada jalankan pertama masing-masing dan setidaknya dua kali lebih cepat ketika membandingkan berjalan (cache) berikutnya.
Dijeda sampai pemberitahuan lebih lanjut.
11
Jika dia hanya menginginkan direktori saat ini, bukan seluruh pohon secara rekursif, dia dapat menambahkan opsi -maxdepth 1 untuk ditemukan.
igustin
3
Sepertinya alasannya findlebih cepat daripada lskarena cara Anda menggunakan ls. Jika Anda berhenti menyortir, lsdan findmemiliki kinerja serupa.
Christopher Schultz
17

temukan, ls dan perl diuji terhadap 40.000 file: kecepatan yang sama (meskipun saya tidak mencoba menghapus 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

dan dengan perl opendir / readdir, waktu yang sama:

[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

Catatan: Saya menggunakan / bin / ls -f untuk memastikan mem-bypass opsi alias yang mungkin sedikit melambat dan -f untuk menghindari pemesanan file. ls tanpa -f dua kali lebih lambat dari find / perl kecuali jika ls digunakan dengan -f, tampaknya waktu yang sama:

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

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

Saya juga ingin memiliki beberapa skrip untuk menanyakan sistem file secara langsung tanpa semua informasi yang tidak perlu.

tes berdasarkan jawaban Peter van der Heijden, glenn jackman dan mark4o.

Thomas

Thomas
sumber
5
Anda harus menghapus cache di antara tes. Pertama kali saya menjalankan ls -l | wc -lfolder pada HDD 2.5 "eksternal dengan file 1M, dibutuhkan sekitar 3 menit untuk operasi untuk menyelesaikan. Kedua kalinya dibutuhkan 12 detik IIRC. Juga ini berpotensi berpotensi bergantung pada sistem file Anda juga. Saya menggunakan Btrfs.
Behrang Saeedzadeh
Terima kasih, potongan perl adalah solusi untuk saya. $ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Halaman
5

Anda dapat mengubah output berdasarkan kebutuhan Anda, tetapi di sini ada bash one-liner yang saya tulis untuk menghitung secara rekursif dan melaporkan jumlah file dalam serangkaian direktori bernama numerik.

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

Ini terlihat secara rekursif untuk semua file (bukan direktori) di direktori yang diberikan dan mengembalikan hasilnya dalam format seperti hash. Perubahan sederhana pada perintah find dapat membuat jenis file apa yang Anda cari lebih spesifik, dll.

Menghasilkan sesuatu seperti ini:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,
perkasa
sumber
1
Saya menemukan contoh agak membingungkan. Saya bertanya-tanya mengapa ada angka di sebelah kiri, bukan nama direktori. Terima kasih untuk ini, saya akhirnya menggunakannya dengan beberapa perubahan kecil. (menghitung direktori dan menjatuhkan nama folder dasar. untuk i di $ (ls -1. | sort -n); {echo "$ i => $ (temukan $ {i} | wc -l)";}
TheJacobTaylor
Angka-angka di sebelah kiri adalah nama direktori saya dari data contoh saya. Maaf itu membingungkan.
mightybs
1
ls -1 ${dir}tidak akan berfungsi dengan baik tanpa lebih banyak ruang. Juga, tidak ada jaminan bahwa nama yang dikembalikan oleh lsdapat diteruskan ke find, karena lslolos dari karakter yang tidak dapat dicetak untuk konsumsi manusia. ( mkdir $'oddly\nnamed\ndirectory'jika Anda ingin test case yang sangat menarik). Lihat Mengapa Anda tidak perlu menguraikan output ls (1)
Charles Duffy
4

Anehnya bagi saya, menemukan tulang kosong sangat sebanding dengan ls-f

> time ls -f my_dir | wc -l
17626

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

melawan

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

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

Tentu saja, nilai-nilai pada desimal ketiga bergeser sedikit setiap kali Anda mengeksekusi semua ini, jadi mereka pada dasarnya identik. Namun perhatikan bahwa findmengembalikan satu unit tambahan, karena ia menghitung direktori aktual itu sendiri (dan, seperti yang disebutkan sebelumnya, ls -fmengembalikan dua unit tambahan, karena ia juga menghitung. Dan ..).

Bogdan Stăncescu
sumber
4

Hanya menambahkan ini demi kelengkapan. Jawaban yang benar tentu saja sudah diposting oleh orang lain, tetapi Anda juga bisa mendapatkan hitungan file dan direktori dengan program tree.

Jalankan perintah tree | tail -n 1untuk mendapatkan baris terakhir, yang akan mengatakan sesuatu seperti "763 direktori, 9290 file". Ini menghitung file dan folder secara rekursif, tidak termasuk file tersembunyi, yang dapat ditambahkan dengan bendera -a. Untuk referensi, butuh 4,8 detik di komputer saya, untuk pohon menghitung seluruh dir rumah saya, yang merupakan 24.777 direktori, 238680 file. find -type f | wc -lbutuh 5,3 detik, setengah detik lebih lama, jadi saya pikir pohon cukup kompetitif dari segi kecepatan.

Selama Anda tidak memiliki subfolder, tree adalah cara cepat dan mudah untuk menghitung file.

Juga, dan murni untuk bersenang-senang, Anda dapat menggunakan tree | grep '^├'hanya menampilkan file / folder di direktori saat ini - ini pada dasarnya adalah versi yang jauh lebih lambat ls.

Benubird
sumber
Brew install tailuntuk OS X.
The Unfun Cat
@TheUnfunCat tailseharusnya sudah diinstal pada sistem Mac OS X Anda.
Christopher Schultz
4

Hitungan File Linux Cepat

Hitungan file linux tercepat yang saya tahu adalah

locate -c -r '/home'

Tidak ada kebutuhan untuk memohon grep! Tetapi seperti yang disebutkan Anda harus memiliki database baru (diperbarui setiap hari oleh pekerjaan cron, atau manual oleh sudo updatedb).

Dari man loc

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

Tambahan Anda harus tahu bahwa itu juga menghitung direktori sebagai file!


BTW: Jika Anda ingin ikhtisar file dan direktori pada jenis sistem Anda

locate -S

Ini menghasilkan jumlah direktori, file dll.

abu_bua
sumber
perhatikan bahwa Anda harus memastikan bahwa basis datanya mutakhir
phuclv
1
LOL jika Anda sudah memiliki semua penghitungan dalam basis data, maka Anda dapat menghitung dengan cepat. :)
Christopher Schultz
3

Menulis ini di sini karena saya tidak memiliki poin reputasi yang cukup untuk mengomentari jawaban, tetapi saya diizinkan untuk meninggalkan jawaban saya sendiri jawaban , yang tidak masuk akal. Bagaimanapun...

Mengenai jawaban oleh Christopher Schultz , saya sarankan mengubah stat ke lstat dan mungkin menambahkan batas-cek untuk menghindari 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;
}

Saran untuk menggunakan lstat adalah untuk menghindari symlink berikut yang dapat menyebabkan siklus jika direktori berisi symlink ke direktori induk.

FlyingCodeMonkey
sumber
2
Modifikasi karena penggunaan lstatadalah saran yang bagus dan Anda pantas mendapatkan karma untuk itu. Saran ini dimasukkan ke dalam kode saya yang diposting di atas dan, sekarang, di GitHub.
Christopher Schultz
2

Anda bisa mencoba jika menggunakan opendir()dan readdir()di Perllebih cepat. Untuk contoh fungsi tersebut lihat di sini

Peter van der Heijden
sumber
2
penggunaan: perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar (@files) '
glenn jackman
2

Jawaban di sini lebih cepat daripada hampir semua hal lain di halaman ini untuk direktori yang sangat besar, sangat bersarang:

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

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

ck_
sumber
1
Bagus. Karena Anda sudah memiliki db terbaru dari semua file, tidak perlu melakukannya lagi. Namun sayangnya, Anda harus memastikan bahwa perintah updatedb telah berjalan dan selesai untuk metode ini.
Chris Reid
kamu tidak perlu grep. Gunakan locate -c -r '/path'seperti dalam solusi
abu_bua
2

Saya datang ke sini ketika mencoba menghitung file dalam dataset ~ 10K folder dengan ~ 10K masing-masing file. Masalah dengan banyak pendekatan adalah bahwa mereka secara implisit stat file 100M, yang membutuhkan waktu lama.

Saya mengambil kebebasan untuk memperluas pendekatan oleh christopher-schultz sehingga mendukung lewat direktori melalui args (pendekatan rekursifnya menggunakan stat juga).

Masukkan yang berikut ke dalam 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;
}

Setelah gcc -o dircnt_args dircnt_args.cAnda dapat memanggilnya seperti ini:

dircnt_args /your/dirs/*

Pada 100M file dalam folder 10K di atas selesai dengan cukup cepat (~ 5 mnt untuk menjalankan pertama, tindak lanjuti cache: ~ 23 dtk).

Satu-satunya pendekatan lain yang selesai dalam waktu kurang dari satu jam itu ls dengan sekitar 1 menit pada cache: ls -f /your/dirs/* | wc -l. Hitungan dimatikan oleh beberapa baris baru per dir meskipun ...

Selain yang diharapkan, tidak ada upaya saya dengan findkembali dalam waktu satu jam: - /

Jörn Hees
sumber
Untuk seseorang yang bukan programmer C, dapatkah Anda menjelaskan mengapa ini lebih cepat, dan bagaimana ia bisa mendapatkan jawaban yang sama tanpa melakukan hal yang sama?
mlissner
Anda tidak perlu menjadi seorang programmer C, cukup pahami apa artinya membuat stat suatu file dan bagaimana direktori direpresentasikan: direktori pada dasarnya adalah daftar nama file dan inode. Jika Anda membuat stat file Anda mengakses inode yang berada di suatu tempat di drive untuk misalnya mendapatkan info seperti ukuran file, izin, .... Jika Anda hanya tertarik dengan jumlah per dir, Anda tidak perlu mengakses info inode, yang mungkin menghemat banyak waktu.
Jörn Hees
Segfault ini pada Oracle linux, versi gcc 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... jalur relatif dan fs jarak jauh tampaknya menjadi penyebabnya
Rondo
2

Cara tercepat di linux (pertanyaannya ditandai sebagai linux), adalah menggunakan panggilan sistem langsung. Berikut adalah program kecil yang menghitung file (hanya, tanpa dir) di direktori. Anda dapat menghitung jutaan file dan ini sekitar 2,5 kali lebih cepat dari "ls -f" dan sekitar 1,3-1,5 kali lebih cepat dari jawaban 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: Itu tidak rekursif tetapi Anda bisa memodifikasinya untuk mencapainya.

Nikolay Dimitrov
sumber
1
Saya tidak yakin saya setuju bahwa ini lebih cepat. Saya belum menelusuri semua yang dilakukan oleh kompiler dengan opendir/ readdir, tapi saya menduga itu bermuara pada kode yang hampir sama pada akhirnya. Membuat panggilan sistem dengan cara itu juga tidak portabel dan, karena Linux ABI tidak stabil, program yang dikompilasi pada satu sistem tidak dijamin berfungsi dengan baik pada sistem lain (walaupun itu saran yang cukup baik untuk mengkompilasi apa pun dari sumber pada sistem * NIX IMO ). Jika kecepatan adalah kunci, ini adalah solusi yang baik jika itu benar-benar meningkatkan kecepatan - saya belum membandingkan program secara terpisah.
Christopher Schultz
1

lsmenghabiskan lebih banyak waktu menyortir nama file, menggunakan -funtuk menonaktifkan penyortiran akan menghemat waktu:

ls -f | wc -l

atau Anda dapat menggunakan find:

find . -type f | wc -l
Mohammad Anini
sumber
0

Saya menyadari bahwa tidak menggunakan dalam pemrosesan memori ketika Anda memiliki sejumlah besar data lebih cepat daripada "memipakan" perintah. Jadi saya menyimpan hasilnya ke file dan setelah menganalisisnya

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l
Marcelo Luiz Onhate
sumber
ini bukan solusi tercepat karena hard disk sangat lambat. Ada cara lain yang lebih efisien yang diposting tahun sebelum Anda
phuclv
0

Anda harus menggunakan "getdents" sebagai ganti ls / find

Berikut ini adalah satu artikel yang sangat bagus yang menggambarkan pendekatan getdents.

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

Ini ekstraknya:

ls dan praktis setiap metode lain untuk mendaftarkan direktori (termasuk python os.listdir, find.) bergantung pada libc readdir (). Namun readdir () hanya membaca 32K entri direktori pada suatu waktu, yang berarti bahwa jika Anda memiliki banyak file di direktori yang sama (mis. 500M entri direktori) akan memakan waktu sangat lama untuk membaca semua entri direktori , terutama pada disk yang lambat. Untuk direktori yang berisi banyak file, Anda harus menggali lebih dalam daripada alat yang mengandalkan readdir (). Anda perlu menggunakan getdents () syscall secara langsung, daripada metode pembantu dari libc.

Kita dapat menemukan kode C untuk mendaftar file menggunakan getdents () dari sini :

Ada dua modifikasi yang perlu Anda lakukan agar daftar semua file dalam direktori dengan cepat.

Pertama, tingkatkan ukuran buffer dari X menjadi sekitar 5 megabita.

#define BUF_SIZE 1024*1024*5

Kemudian modifikasi loop utama di mana ia mencetak informasi tentang setiap file dalam direktori untuk melewati entri dengan inode == 0. Saya melakukan ini dengan menambahkan

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

Dalam kasus saya, saya juga benar-benar hanya peduli tentang nama file di direktori jadi saya juga menulis ulang pernyataan printf () untuk hanya mencetak nama file.

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

Kompilasi (tidak perlu perpustakaan eksternal, jadi sangat mudah dilakukan)

gcc listdir.c -o listdir

Sekarang jalankan

./listdir [directory with insane number of files]
Dev123
sumber
Perhatikan bahwa Linux melakukan baca-depan, jadi readdir()sebenarnya tidak lambat. Saya perlu angka yang kuat sebelum saya percaya bahwa ada baiknya membuang portabilitas untuk mendapatkan kinerja ini.
fuz
-1

Saya lebih suka perintah berikut untuk melacak perubahan jumlah file dalam direktori.

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

Perintah akan membuat jendela terbuka untuk melacak jumlah file yang ada di direktori dengan kecepatan refresh 0,1 detik.

Anoop Toffy
sumber
apakah kamu yakin itu ls | wc -l akan selesai untuk folder dengan ribuan atau jutaan file dalam 0,01? bahkan Anda lssangat tidak efisien dibandingkan dengan solusi lain. Dan OP hanya ingin mendapatkan hitungan, tidak duduk di sana melihat output berubah
phuclv
Baik. Baik. Saya menemukan solusi elegan yang cocok untuk saya. Saya ingin berbagi yang sama, maka lakukanlah. Saya tidak tahu perintah 'ls' di linux sangat tidak efisien. Apa yang kamu gunakan bukannya itu? Dan 0,01 adalah kecepatan refresh. Bukan waktunya. jika Anda belum menggunakan arloji, silakan merujuk halaman manual.
Anoop Toffy
baik saya memang membaca watch manual setelah komentar itu dan melihat bahwa 0,01s (bukan 0,1s) adalah angka yang tidak realistis karena kecepatan refresh sebagian besar layar PC hanya 60Hz, dan ini tidak menjawab pertanyaan dengan cara apa pun. OP bertanya tentang "Jumlah File Linux Cepat untuk sejumlah besar file". Anda juga tidak membaca jawaban yang tersedia sebelum memposting
phuclv
Saya memang membaca jawabannya. Tapi apa yang saya posting adalah cara melacak perubahan jumlah file dalam direktori. misalnya: saat menyalin file dari satu lokasi ke lokasi lain jumlah file membuat perubahan. dengan metode yang saya poster satu dapat melacak itu. Saya setuju bahwa tulisan yang saya buat tidak mengubah atau memperbaiki tulisan sebelumnya.
Anoop Toffy
-2

10 direktori pertama dengan no tertinggi file.

dir=/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$(find ${dir}${i} \
    -type f | wc -l) => $i,"; } | sort -nr | head -10
pengguna2546874
sumber
3
Ini tentu terlihat sangat mirip dengan jawaban (dengan bug yang sama) yang ditulis oleh perkasa . Jika Anda akan memperluas atau memodifikasi kode yang ditulis oleh orang lain, mengkreditkannya sudah tepat. Memahami kode yang Anda gunakan dalam jawaban Anda cukup untuk mengidentifikasi dan memperbaiki bug-nya bahkan lebih tepat.
Charles Duffy