En hızlı yol, bu gibi özel olarak tasarlanmış bir programdır:
#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;
}
Önbelleğe bakılmaksızın testlerimden, önbellek tabanlı veri çarpıklığını önlemek için her birini yaklaşık 50 kez aynı dizine karşı tekrar tekrar çalıştırdım ve kabaca aşağıdaki performans numaralarını aldım (gerçek saatte):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Sonuncusu dircnt
, yukarıdaki kaynaktan derlenen programdır.
DÜZENLE 2016-09-26
Yoğun talep nedeniyle, bu programı yinelemeli olarak yeniden yazdım, bu yüzden alt dizinlere düşecek ve dosyaları ve dizinleri ayrı olarak saymaya devam edecek.
Bazı insanlar tüm bunların nasıl yapılacağını bilmek istediği açık olduğundan, neler olduğunu açık hale getirmek için kodda bir sürü yorum var. Bunu yazdım ve 64-bit Linux üzerinde test, ama gerektiği Microsoft Windows dahil olmak üzere herhangi POSIX uyumlu sistem üzerinde çalışıyoruz. Hata raporları açıktır; AIX veya OS / 400'nizde veya başka herhangi bir şeyde çalıştıramazsanız bunu güncellemekten mutluluk duyarız.
Gördüğünüz gibi , orijinalden çok daha karmaşık ve zorunlu olarak öyle: kodun çok karmaşık hale gelmesini istemiyorsanız (örneğin bir alt dizin yığınını yönetme ve bunu tek bir döngüde işleme koyma) en az bir işlev yinelemeli olarak çağrılmalıdır. Dosya türlerini kontrol etmemiz gerektiğinden, farklı işletim sistemleri, standart kütüphaneler, vb. Arasındaki farklar devreye girer, bu yüzden derleyeceği herhangi bir sistemde kullanılabilir olmaya çalışan bir program yazdım.
Çok az hata kontrolü var ve count
fonksiyonun kendisi gerçekten hataları rapor etmiyor. Gerçekten başarısız olabilen tek çağrılar opendir
ve stat
(eğer şanslı değilseniz ve dirent
zaten dosya türünü içeren bir sisteminiz varsa ). Alt dizin yol adlarının toplam uzunluğunu kontrol etme konusunda paranoyak değilim, ancak teorik olarak, sistem daha uzun olan herhangi bir yol adına izin vermemelidir PATH_MAX
. Endişeler varsa, bunu düzeltebilirim, ancak C yazmayı öğrenen birine açıklanması gereken sadece daha fazla kod var.
#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
@FlyingCodeMonkey tarafından önerilen iki değişiklik ekledim:
- Yerine
lstat
kullanın stat
. Bu, taradığınız dizinde işaretlenmiş dizinleriniz varsa programın davranışını değiştirir. Önceki davranış, (bağlantılı) alt dizinin dosya sayısının toplam sayıya eklenmesiydi; yeni davranış, bağlantılı dizinin tek bir dosya olarak sayılması ve içeriğinin sayılmamasıdır.
- Bir dosyanın yolu çok uzunsa, bir hata mesajı verilir ve program durur.
DÜZENLE 2017-06-29
Herhangi bir şansla, bu cevabın son düzenlemesi olacak :)
Kodu almayı biraz daha kolaylaştırmak için bu kodu bir GitHub deposuna kopyaladım (kopyala / yapıştır yerine, sadece kaynağı indirebilirsiniz ), ayrıca herkesin bir çekme göndererek bir değişiklik önermesini kolaylaştırır GitHub'dan talep.
Kaynak Apache Lisansı 2.0 altındadır. Yamalar * hoş geldiniz!
- "yama" benim gibi yaşlı insanların "çekme isteği" dediği şeydir.