วิธีที่เร็วที่สุดคือโปรแกรมที่สร้างขึ้นโดยเฉพาะเช่นนี้:
#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;
}
จากการทดสอบของฉันโดยไม่คำนึงถึงแคชฉันรันแต่ละรายการประมาณ 50 ครั้งต่อไดเรกทอรีเดียวกันซ้ำแล้วซ้ำเล่าเพื่อหลีกเลี่ยงการบิดเบือนข้อมูลที่อิงกับแคชและฉันได้รับตัวเลขประสิทธิภาพโดยประมาณต่อไปนี้ (ตามเวลานาฬิกาจริง):
ls -1 | wc - 0:01.67
ls -f1 | wc - 0:00.14
find | wc - 0:00.22
dircnt | wc - 0:00.04
สุดท้ายdircnt
คือโปรแกรมที่รวบรวมจากแหล่งข้อมูลข้างต้น
แก้ไข 26-09-2016
เนื่องจากความต้องการที่ได้รับความนิยมฉันจึงเขียนโปรแกรมนี้ขึ้นมาใหม่เพื่อให้เรียกซ้ำดังนั้นโปรแกรมจะลดลงในไดเรกทอรีย่อยและนับไฟล์และไดเรกทอรีแยกกัน
เนื่องจากเห็นได้ชัดว่าบางคนต้องการทราบวิธีการทั้งหมดนี้ฉันจึงมีความคิดเห็นมากมายในโค้ดเพื่อพยายามทำให้ชัดเจนว่าเกิดอะไรขึ้น ฉันเขียนสิ่งนี้และทดสอบบน Linux 64 บิต แต่ควรทำงานได้กับระบบที่รองรับ POSIX รวมถึง Microsoft Windows ยินดีต้อนรับรายงานข้อผิดพลาด ฉันยินดีที่จะอัปเดตสิ่งนี้หากคุณไม่สามารถทำงานบน AIX หรือ OS / 400 หรืออะไรก็ได้
ที่คุณสามารถดูมันมากความซับซ้อนมากขึ้นกว่าเดิมและจำเป็นต้องให้: อย่างน้อยหนึ่งฟังก์ชั่นจะต้องมีอยู่จะเรียกว่าซ้ำถ้าคุณต้องการรหัสที่จะกลายเป็นความซับซ้อนมาก (เช่นการจัดการกองไดเรกทอรีย่อยและการประมวลผลที่ในวงเดียว) เนื่องจากเราต้องตรวจสอบประเภทไฟล์ความแตกต่างระหว่าง OS ต่างๆไลบรารีมาตรฐาน ฯลฯ จึงเข้ามามีบทบาทดังนั้นฉันจึงเขียนโปรแกรมที่พยายามจะใช้งานได้กับระบบใด ๆ
มีการตรวจสอบข้อผิดพลาดน้อยมากและcount
ตัวฟังก์ชันเองก็ไม่ได้รายงานข้อผิดพลาดจริงๆ การโทรเท่านั้นที่สามารถล้มเหลวได้คือopendir
และstat
(หากคุณไม่โชคดีและมีระบบที่dirent
มีประเภทไฟล์อยู่แล้ว) ฉันไม่ paranoid เกี่ยวกับการตรวจสอบความยาวรวมของ pathnames subdir แต่ในทางทฤษฎีระบบไม่ควรอนุญาตให้ชื่อเส้นทางใด ๆ PATH_MAX
ที่มีความยาวเกินกว่า หากมีข้อกังวลฉันสามารถแก้ไขได้ แต่เป็นเพียงโค้ดเพิ่มเติมที่ต้องอธิบายให้คนที่เรียนรู้ที่จะเขียน C โปรแกรมนี้มีจุดมุ่งหมายเพื่อเป็นตัวอย่างของการดำน้ำในไดเรกทอรีย่อยแบบวนซ้ำ
#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
struct filecount {
long dirs;
long files;
};
void count(char *path, struct filecount *counts) {
DIR *dir;
struct dirent *ent;
char subpath[PATH_MAX];
#if !defined ( _DIRENT_HAVE_D_TYPE )
struct stat statbuf;
#endif
dir = opendir(path);
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;
}
#if defined ( _DIRENT_HAVE_D_TYPE )
if(DT_DIR == ent->d_type) {
#else
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
if(0 == strcmp("..", ent->d_name) || 0 == strcmp(".", ent->d_name)) {
} else {
sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
counts->dirs++;
count(subpath, counts);
}
} else {
counts->files++;
}
}
closedir(dir);
}
int main(int argc, char *argv[]) {
struct filecount counts;
counts.files = 0;
counts.dirs = 0;
count(argv[1], &counts);
if(0 < counts.files || 0 < counts.dirs) {
printf("%s contains %ld files and %ld directories\n", argv[1], counts.files, counts.dirs);
}
return 0;
}
แก้ไข 2017-01-17
ฉันได้รวมการเปลี่ยนแปลงสองอย่างที่แนะนำโดย @FlyingCodeMonkey:
- ใช้
lstat
แทนstat
. สิ่งนี้จะเปลี่ยนลักษณะการทำงานของโปรแกรมหากคุณมีไดเร็กทอรี symlinked ในไดเร็กทอรีที่คุณกำลังสแกน พฤติกรรมก่อนหน้านี้คือไดเร็กทอรีย่อย (ที่เชื่อมโยง) จะมีการเพิ่มจำนวนไฟล์ลงในจำนวนโดยรวม ลักษณะการทำงานใหม่คือไดเร็กทอรีที่เชื่อมโยงจะนับเป็นไฟล์เดียวและเนื้อหาจะไม่ถูกนับ
- หากเส้นทางของไฟล์ยาวเกินไปข้อความแสดงข้อผิดพลาดจะปรากฏขึ้นและโปรแกรมจะหยุดทำงาน
แก้ไข 2017-06-29
ด้วยความโชคดีนี่จะเป็นการแก้ไขครั้งสุดท้ายของคำตอบนี้ :)
ฉันได้คัดลอกโค้ดนี้ลงในที่เก็บ GitHubเพื่อให้ง่ายต่อการรับโค้ด (แทนที่จะคัดลอก / วางคุณสามารถดาวน์โหลดซอร์สได้ ) แถมยังช่วยให้ทุกคนแนะนำการแก้ไขได้ง่ายขึ้นโดยการส่งการดึง - ขอจาก GitHub
แหล่งที่มามีอยู่ใน Apache License 2.0 แพทช์* ยินดีต้อนรับ!
- "ปะ" คือสิ่งที่คนแก่อย่างฉันเรียกว่า "pull request"