Cách nhanh nhất là một chương trình được xây dựng có mục đích, như thế này:
#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;
}
Từ thử nghiệm của tôi mà không liên quan đến bộ đệm, tôi đã chạy từng cái khoảng 50 lần cho cùng một thư mục, lặp đi lặp lại, để tránh sai lệch dữ liệu dựa trên bộ đệm và tôi đã nhận được các số hiệu suất sau (trong thời gian thực):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
Cái cuối cùng đó dircnt
là chương trình được tổng hợp từ nguồn trên.
CHỈNH SỬA 2016-09-26
Do nhu cầu phổ biến, tôi đã viết lại chương trình này thành đệ quy, vì vậy nó sẽ rơi vào các thư mục con và tiếp tục đếm các tệp và thư mục riêng biệt.
Vì rõ ràng một số người muốn biết làm thế nào để làm tất cả điều này, tôi có rất nhiều ý kiến trong mã để cố gắng làm cho nó rõ ràng những gì đang diễn ra. Tôi đã viết cái này và thử nó trên Linux 64 bit, nhưng nó sẽ hoạt động trên mọi hệ thống tương thích POSIX, bao gồm cả Microsoft Windows. Báo cáo lỗi được chào đón; Tôi rất vui khi cập nhật điều này nếu bạn không thể làm cho nó hoạt động trên AIX hoặc OS / 400 của bạn hoặc bất cứ điều gì.
Như bạn có thể thấy, phần này nhiều hơn phức tạp hơn so với bản gốc và nhất thiết phải như vậy: ít nhất một chức năng phải tồn tại được gọi là đệ quy, trừ khi bạn muốn mã trở nên rất phức tạp (ví dụ như quản lý một chồng thư mục con và chế biến mà trong một vòng lặp đơn). Vì chúng tôi phải kiểm tra các loại tệp, sự khác biệt giữa các HĐH khác nhau, thư viện chuẩn, v.v., vì vậy tôi đã viết một chương trình cố gắng sử dụng được trên bất kỳ hệ thống nào mà nó sẽ biên dịch.
Có rất ít kiểm tra lỗi và count
bản thân hàm không thực sự báo cáo lỗi. Các cuộc gọi duy nhất thực sự có thể thất bại là opendir
và stat
(nếu bạn không may mắn và đã có một hệ thống dirent
chứa loại tệp). Tôi không hoang tưởng về việc kiểm tra tổng chiều dài của tên đường dẫn con, nhưng về mặt lý thuyết, hệ thống không nên cho phép bất kỳ tên đường dẫn nào dài hơn PATH_MAX
. Nếu có lo ngại, tôi có thể khắc phục điều đó, nhưng đó chỉ là nhiều mã cần được giải thích cho người học viết C. Chương trình này được dự định là một ví dụ về cách đi sâu vào các thư mục con theo cách đệ quy.
#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
Tôi đã kết hợp hai thay đổi được đề xuất bởi @FellingCodeMonkey:
- Sử dụng
lstat
thay vì stat
. Điều này sẽ thay đổi hành vi của chương trình nếu bạn có các thư mục được liên kết trong thư mục bạn đang quét. Hành vi trước đó là thư mục con (được liên kết) sẽ có số tập tin được thêm vào tổng số; hành vi mới là thư mục được liên kết sẽ được tính là một tệp duy nhất và nội dung của nó sẽ không được tính.
- Nếu đường dẫn của tệp quá dài, thông báo lỗi sẽ được phát ra và chương trình sẽ tạm dừng.
EDIT 2017-06-29
Với bất kỳ may mắn nào, đây sẽ là bản chỉnh sửa cuối cùng của câu trả lời này :)
Tôi đã sao chép mã này vào kho lưu trữ GitHub để giúp lấy mã dễ dàng hơn một chút (thay vì sao chép / dán, bạn chỉ có thể tải xuống nguồn ), cộng với việc mọi người đề xuất sửa đổi dễ dàng hơn bằng cách gửi một lần kéo - yêu cầu từ GitHub.
Nguồn có sẵn theo Giấy phép Apache 2.0. Bản vá * chào mừng!
- "Bản vá" là thứ mà những người già như tôi gọi là "yêu cầu kéo".