Đếm tệp Linux nhanh cho một số lượng lớn tệp


136

Tôi đang cố gắng tìm ra cách tốt nhất để tìm số lượng tệp trong một thư mục cụ thể khi có số lượng tệp rất lớn (> 100.000).

Khi có nhiều tệp như vậy, việc thực hiện ls | wc -lmất khá nhiều thời gian để thực thi. Tôi tin rằng điều này là do nó trả về tên của tất cả các tệp. Tôi đang cố gắng chiếm ít đĩa IO nhất có thể.

Tôi đã thử nghiệm với một số kịch bản shell và Perl nhưng không có kết quả. Có ý kiến ​​gì không?


2
đảm bảo rằng "ls" của bạn là / usr / bin / ls và không phải là bí danh cho một cái gì đó lạ hơn.
glenn jackman

Câu hỏi tương tự với câu trả lời thú vị ở đây: serverfault.com/questions/205071/ từ
Aidan

Giá trị của nó chỉ ra rằng hầu hết nếu không phải tất cả các giải pháp được trình bày cho câu hỏi này đều không dành riêng cho Linux , nhưng khá chung cho tất cả các hệ thống giống như * NIX. Có lẽ loại bỏ thẻ "Linux" là phù hợp.
Christopher Schultz

Câu trả lời:


188

Theo mặc định, lssắp xếp các tên, có thể mất một lúc nếu có nhiều tên. Ngoài ra, sẽ không có đầu ra cho đến khi tất cả các tên được đọc và sắp xếp. Sử dụng ls -ftùy chọn để tắt sắp xếp.

ls -f | wc -l

Lưu ý rằng điều này cũng sẽ cho phép -a, vì vậy ., ..tập tin, và khác bắt đầu với .sẽ được tính.


11
+1 Và tôi nghĩ rằng tôi biết tất cả mọi thứ cần biết ls.
mob

5
ZOMG. Sắp xếp các dòng 100K không là gì - so với stat()cuộc gọi lsthực hiện trên mỗi tệp. findkhông stat()vì thế nó hoạt động nhanh hơn.
Dummy00001

12
ls -fkhông stat()một trong hai. Nhưng tất nhiên cả hai lsfindgọi stat()khi các tùy chọn nhất định được sử dụng, chẳng hạn như ls -lhoặc find -mtime.
mark4o

7
Đối với ngữ cảnh, việc này mất 1-2 phút để đếm 2,5 triệu jpg trên hộp Slicehost nhỏ.
philfreo

6
Nếu bạn muốn thêm thư mục con vào số đếm, hãy làmls -fR | wc -l
Ryan Walls

62

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 đó dircntlà 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à countbả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à opendirstat(nếu bạn không may mắn và đã có một hệ thống direntchứ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:

  1. Sử dụng lstatthay 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.
  2. 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".

2
Tuyệt vời! cảm ơn! Và đối với những người không biết: bạn có thể biên dịch mã trên trong thiết bị đầu cuối: gcc -o dircnt dircnt.cvà việc sử dụng là như thế này./dircnt some_dir
aesede 19/03/2015

Có một cách dễ dàng để làm cho đệ quy này?
ck_

@ck_ Chắc chắn, điều này có thể dễ dàng được thực hiện đệ quy. Bạn có cần giúp đỡ với giải pháp, hoặc bạn muốn tôi viết toàn bộ?
Christopher Schultz

1
@ChristopherSchultz, điểm chuẩn bạn đã đăng ở trên - thư mục trong câu hỏi lớn đến mức nào?
Dom Vinyard

1
Tôi thực sự muốn sử dụng cái này trong Python vì vậy tôi đã đóng gói nó dưới dạng gói ffcount . Cảm ơn đã cung cấp mã có sẵn @ChristopherSchultz!
GjjvdBurg

35

Bạn đã thử tìm chưa? Ví dụ:

find . -name "*.ext" | wc -l

1
Điều này sẽ đệ quy tìm tập tin trong thư mục hiện tại.
mark4o

Trên hệ thống của tôi, find /usr/share | wc -l(~ 137.000 tệp) nhanh hơn khoảng 25% so với ls -R /usr/share | wc -l(~ 160.000 dòng bao gồm tên thư mục, tổng số thư mục và dòng trống) trong lần chạy đầu tiên của mỗi và ít nhất là nhanh gấp đôi khi so sánh các lần chạy tiếp theo (được lưu trong bộ nhớ cache).
Tạm dừng cho đến khi có thông báo mới.

11
Nếu anh ta chỉ muốn thư mục hiện tại, không phải toàn bộ cây đệ quy, anh ta có thể thêm tùy chọn -maxdepth 1 để tìm.
igustin

3
Có vẻ như lý do findnhanh hơn lslà vì cách bạn đang sử dụng ls. Nếu bạn dừng sắp xếp, lsfindcó hiệu suất tương tự.
Christopher Schultz

17

tìm, ls và perl đã thử nghiệm với 40 000 tệp: cùng tốc độ (mặc dù tôi không thử xóa bộ đệm):

[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

và với perl opendir / readdir, cùng một lúc:

[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

lưu ý: Tôi đã sử dụng / bin / ls -f để đảm bảo bỏ qua tùy chọn bí danh có thể làm chậm một chút và -f để tránh sắp xếp tệp. ls không có -f chậm hơn hai lần so với find / perl trừ khi ls được sử dụng với -f, có vẻ như là cùng một lúc:

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

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

Tôi cũng muốn có một số kịch bản để yêu cầu hệ thống tập tin trực tiếp mà không có tất cả các thông tin không cần thiết.

các bài kiểm tra dựa trên câu trả lời của Peter van der Heijden, glenn jackman và mark4o.

Thomas


5
Bạn chắc chắn nên xóa bộ nhớ cache giữa các bài kiểm tra. Lần đầu tiên tôi chạy ls -l | wc -ltrên một thư mục trên ổ cứng 2,5 "bên ngoài với các tệp 1M, phải mất khoảng 3 phút để hoạt động kết thúc. Lần thứ hai phải mất 12 giây IIRC. Ngoài ra, điều này cũng có thể phụ thuộc vào hệ thống tệp của bạn. đã sử dụng Btrfs.
Behrang Saeedzadeh

Cảm ơn bạn, perl snippet là giải pháp cho tôi. $ 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

Bạn có thể thay đổi đầu ra dựa trên yêu cầu của bạn, nhưng đây là một bash one-liner tôi đã viết để đếm đệ quy và báo cáo số lượng tệp trong một loạt các thư mục được đặt tên bằng số.

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

Điều này trông đệ quy cho tất cả các tệp (không phải thư mục) trong thư mục đã cho và trả về kết quả ở định dạng giống như hàm băm. Các chỉnh sửa đơn giản cho lệnh find có thể làm cho loại tệp bạn đang tìm kiếm để đếm cụ thể hơn, v.v.

Kết quả trong một cái gì đó như thế này:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,

1
Tôi tìm thấy ví dụ một chút khó hiểu. Tôi đã tự hỏi tại sao có số ở bên trái, thay vì tên thư mục. Cảm ơn bạn vì điều này, cuối cùng tôi đã sử dụng nó với một vài điều chỉnh nhỏ. .. (thư mục đếm và thả các cơ sở thư mục tên for i in $ (ls -1 | sort -n); {echo "$ i => $ (tìm $ {i} | wc -l)";}
TheJacobTaylor

Các số bên trái là tên thư mục của tôi từ dữ liệu ví dụ của tôi. Xin lỗi đó là khó hiểu.
hùng mạnh

1
ls -1 ${dir}Sẽ không hoạt động đúng nếu không có nhiều không gian hơn. Ngoài ra, không có gì đảm bảo rằng tên được trả về lscó thể được chuyển đến find, vì lsthoát khỏi các ký tự không thể in được cho con người. ( mkdir $'oddly\nnamed\ndirectory'nếu bạn muốn một trường hợp thử nghiệm đặc biệt thú vị). Xem lý do tại sao bạn không nên phân tích đầu ra của ls (1)
Charles Duffy

4

Đáng ngạc nhiên đối với tôi, một phát hiện xương trần rất nhiều so sánh với ls -f

> time ls -f my_dir | wc -l
17626

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

đấu với

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

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

Tất nhiên, các giá trị trên vị trí thập phân thứ ba thay đổi một chút mỗi khi bạn thực hiện bất kỳ giá trị nào trong số này, vì vậy về cơ bản chúng giống hệt nhau. Tuy nhiên, lưu ý rằng findtrả về một đơn vị bổ sung, bởi vì nó đếm chính thư mục thực tế (và, như đã đề cập trước đó, ls -ftrả về hai đơn vị bổ sung, vì nó cũng tính. Và ..).


4

Chỉ cần thêm điều này cho sự hoàn chỉnh. Câu trả lời đúng tất nhiên đã được đăng bởi người khác, nhưng bạn cũng có thể nhận được số lượng tệp và thư mục với chương trình cây.

Chạy lệnh tree | tail -n 1để lấy dòng cuối cùng, nó sẽ nói một cái gì đó như "763 thư mục, 9290 tệp". Điều này đếm các tệp và thư mục theo cách đệ quy, ngoại trừ các tệp ẩn, có thể được thêm bằng cờ -a. Để tham khảo, phải mất 4,8 giây trên máy tính của tôi, để cây đếm toàn bộ thư mục nhà của tôi, đó là 24777 thư mục, 238680 tệp. find -type f | wc -lmất 5,3 giây, lâu hơn nửa giây, vì vậy tôi nghĩ rằng cây khá cạnh tranh về tốc độ.

Miễn là bạn không có bất kỳ thư mục con nào, cây là cách nhanh chóng và dễ dàng để đếm các tệp.

Ngoài ra, và hoàn toàn cho sự thú vị của nó, bạn có thể sử dụng tree | grep '^├'để chỉ hiển thị các tệp / thư mục trong thư mục hiện tại - về cơ bản đây là phiên bản chậm hơn nhiều ls.


Brew install tailcho OS X.
The Unun Cat

@TheUnfunCat tailđã được cài đặt trên hệ thống Mac OS X của bạn.
Christopher Schultz

4

Đếm tệp Linux nhanh

Số lượng tập tin linux nhanh nhất mà tôi biết là

locate -c -r '/home'

không cần phải gọi grep! Nhưng như đã đề cập, bạn nên có một cơ sở dữ liệu mới (được cập nhật hàng ngày bởi một công việc định kỳ hoặc bằng tay sudo updatedb).

Từ người đàn ông xác định vị trí

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

Bổ sung bạn nên biết rằng nó cũng tính các thư mục như các tập tin!


BTW: Nếu bạn muốn có một cái nhìn tổng quan về các tệp và thư mục của bạn về loại hệ thống của bạn

locate -S

Nó xuất ra số lượng thư mục, tập tin, vv


lưu ý rằng bạn phải đảm bảo rằng cơ sở dữ liệu được cập nhật
phuclv

1
LOL nếu bạn đã có tất cả số đếm trong cơ sở dữ liệu, thì bạn chắc chắn có thể đếm nhanh. :)
Christopher Schultz

3

Viết điều này ở đây vì tôi không có đủ điểm danh tiếng để bình luận về một câu trả lời, nhưng tôi được phép để lại câu trả lời của riêng mình , điều đó không có ý nghĩa. Dù sao...

Về câu trả lời của Christopher Schultz , tôi khuyên bạn nên thay đổi stat thành lstat và có thể thêm giới hạn kiểm tra để tránh tràn bộ đệm:

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

Gợi ý sử dụng lstat là tránh theo các liên kết tượng trưng có thể dẫn đến chu kỳ nếu một thư mục chứa liên kết tượng trưng đến thư mục mẹ.


2
Sửa đổi bởi vì sử dụng lstatlà một gợi ý tốt và bạn xứng đáng được nghiệp cho nó. Đề xuất này đã được tích hợp vào mã của tôi được đăng ở trên và, bây giờ, trên GitHub.
Christopher Schultz

2

Bạn có thể thử nếu sử dụng opendir()readdir()trong Perllà nhanh hơn. Để biết ví dụ về các chức năng này, hãy nhìn vào đây


2
cách sử dụng: perl -e 'opendir D, "."; @files = readdir D; đóng cửa D; in vô hướng (@files)'
glenn jackman

2

Câu trả lời này ở đây nhanh hơn hầu hết mọi thứ khác trên trang này cho các thư mục rất lớn, rất lồng nhau:

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

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


1
Đẹp. Vì bạn đã có một db cập nhật của tất cả các tệp, nên không cần phải truy cập lại. Nhưng thật không may, bạn phải chắc chắn rằng lệnh updateb đã chạy và hoàn thành cho phương thức này.
Chris Reid

bạn không cần phải grep. Sử dụng locate -c -r '/path'như trong giải pháp của
abu_bua

2

Tôi đến đây khi cố gắng đếm các tập tin trong bộ dữ liệu gồm ~ 10K thư mục với mỗi tập tin ~ 10K. Vấn đề với nhiều cách tiếp cận là chúng hoàn toàn thống kê các tệp 100M, mất nhiều thời gian.

Tôi đã tự do mở rộng cách tiếp cận của christopher-schultz để nó hỗ trợ chuyển các thư mục thông qua args (cách tiếp cận đệ quy của anh ấy cũng sử dụng stat).

Đặt những điều sau đây vào tập tin 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;
}

Sau một thời gian, gcc -o dircnt_args dircnt_args.cbạn có thể gọi nó như thế này:

dircnt_args /your/dirs/*

Trên các tệp 100M trong các thư mục 10K, phần trên hoàn thành khá nhanh (~ 5 phút cho lần chạy đầu tiên, theo dõi trên bộ đệm: ~ 23 giây).

Cách tiếp cận khác chỉ hoàn thành trong vòng chưa đầy một giờ là ls với khoảng 1 phút trên bộ đệm : ls -f /your/dirs/* | wc -l. Số lượng bị tắt bởi một vài dòng mới trên mỗi thư mục mặc dù ...

Ngoài dự kiến, không có nỗ lực nào của tôi được findtrả lại trong vòng một giờ: - /


Đối với ai đó không phải là lập trình viên C, bạn có thể giải thích tại sao điều này sẽ nhanh hơn không và làm thế nào để có thể có được câu trả lời tương tự mà không làm điều tương tự?
mlissner

bạn không cần phải là một lập trình viên C, chỉ cần hiểu ý nghĩa của việc thống kê một tệp và cách các thư mục được thể hiện: các thư mục về cơ bản là danh sách các tên tệp và inodes. Nếu bạn chỉ định một tệp bạn truy cập vào nút ở đâu đó trên ổ đĩa để lấy thông tin như kích thước tệp, quyền, .... Nếu bạn chỉ quan tâm đến số lượng trên mỗi thư mục, bạn không cần truy cập vào thông tin inode, điều này có thể giúp bạn tiết kiệm rất nhiều thời gian.
Jorn Hees

Sự phân tách này trên Oracle linux, phiên bản gcc 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... các đường dẫn tương đối và fs từ xa dường như là nguyên nhân
Rondo

2

Cách nhanh nhất trên linux (câu hỏi được gắn thẻ là linux), là sử dụng cuộc gọi hệ thống trực tiếp. Đây là một chương trình nhỏ đếm các tập tin (chỉ, không có thư mục) trong một thư mục. Bạn có thể đếm hàng triệu tệp và nó nhanh hơn khoảng 2,5 lần so với "ls -f" và nhanh hơn khoảng 1,3-1,5 lần so với câu trả lời của 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: Nó không phải là đệ quy nhưng bạn có thể sửa đổi nó để đạt được điều đó.


1
Tôi không chắc chắn tôi đồng ý rằng điều này nhanh hơn. Tôi đã không theo dõi tất cả mọi thứ mà trình biên dịch thực hiện với opendir/ readdir, nhưng tôi nghi ngờ cuối cùng nó cũng có mã giống như vậy. Thực hiện các cuộc gọi hệ thống theo cách đó cũng không khả dụng và vì Linux ABI không ổn định, một chương trình được biên dịch trên một hệ thống không được đảm bảo để hoạt động chính xác trên hệ thống khác (mặc dù đó là lời khuyên khá tốt để biên dịch mọi thứ từ nguồn trên bất kỳ IMO hệ thống * NIX nào ). Nếu tốc độ là chính, đây là một giải pháp tốt nếu nó thực sự cải thiện tốc độ - tôi đã không điểm chuẩn các chương trình riêng biệt.
Christopher Schultz

1

lsDành nhiều thời gian hơn để sắp xếp tên tệp, sử dụng -fđể vô hiệu hóa việc sắp xếp sẽ đôi khi tiết kiệm:

ls -f | wc -l

hoặc bạn có thể sử dụng find:

find . -type f | wc -l

0

Tôi nhận ra rằng không sử dụng trong xử lý bộ nhớ khi bạn có một lượng dữ liệu khổng lồ nhanh nhất là "đường ống" các lệnh. Vì vậy, tôi đã lưu kết quả vào một tệp và sau khi phân tích nó

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l

Đây không phải là giải pháp nhanh nhất vì đĩa cứng cực kỳ chậm. Có nhiều cách khác hiệu quả hơn đã được đăng nhiều năm trước bạn
phuclv

0

Bạn nên sử dụng "getdents" thay cho ls / find

Đây là một bài viết rất hay mô tả cách tiếp cận getdents.

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

Đây là đoạn trích:

ls và thực tế mọi phương pháp khác để liệt kê một thư mục (bao gồm python os.listdir, find.) dựa vào libc readdir (). Tuy nhiên, readdir () chỉ đọc 32K mục nhập thư mục cùng một lúc, điều đó có nghĩa là nếu bạn có nhiều tệp trong cùng thư mục (tức là 500M mục nhập thư mục), sẽ mất nhiều thời gian để đọc tất cả các mục trong thư mục , đặc biệt là trên một đĩa chậm. Đối với các thư mục chứa một số lượng lớn tệp, bạn sẽ cần đào sâu hơn các công cụ dựa trên readdir (). Bạn sẽ cần sử dụng trực tiếp các tòa nhà getdents (), thay vì các phương thức trợ giúp từ libc.

Chúng ta có thể tìm mã C để liệt kê các tệp bằng getdents () từ đây :

Có hai sửa đổi bạn sẽ cần phải thực hiện để nhanh chóng liệt kê tất cả các tệp trong một thư mục.

Đầu tiên, tăng kích thước bộ đệm từ X lên khoảng 5 megabyte.

#define BUF_SIZE 1024*1024*5

Sau đó sửa đổi vòng lặp chính nơi nó in ra thông tin về từng tệp trong thư mục để bỏ qua các mục có inode == 0. Tôi đã làm điều này bằng cách thêm

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

Trong trường hợp của tôi, tôi cũng thực sự chỉ quan tâm đến tên tệp trong thư mục nên tôi cũng viết lại câu lệnh printf () để chỉ in tên tệp.

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

Biên dịch nó (nó không cần bất kỳ thư viện bên ngoài nào, vì vậy nó cực kỳ đơn giản để làm)

gcc listdir.c -o listdir

Bây giờ chỉ cần chạy

./listdir [directory with insane number of files]

Lưu ý rằng Linux thực hiện đọc trước, vì vậy readdir()không thực sự chậm. Tôi cần con số vững chắc trước khi tôi tin rằng thật đáng để vứt bỏ tính di động để đạt được hiệu suất này.
fuz

-1

Tôi thích lệnh sau để theo dõi các thay đổi về số lượng tệp trong một thư mục.

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

Lệnh sẽ giữ một cửa sổ mở để theo dõi không có tệp nào trong thư mục với tốc độ làm mới 0,1 giây.


Bạn có chắc chắn ls | wc -lsẽ hoàn thành một thư mục có hàng ngàn hoặc hàng triệu tệp trong 0,01 giây không? thậm chí của bạn lslà rất kém hiệu quả so với các giải pháp khác. Và OP chỉ muốn có được số đếm, không ngồi đó nhìn vào đầu ra thay đổi
phuclv

Tốt. Tốt. Tôi tìm thấy một giải pháp thanh lịch phù hợp với tôi. Tôi muốn chia sẻ tương tự, do đó đã làm. Tôi không biết lệnh 'ls' trong linux rất kém hiệu quả. Bạn đang sử dụng cái gì thay vì đó? Và 0,01s là tốc độ làm mới. Không phải lúc. nếu bạn không sử dụng đồng hồ, vui lòng tham khảo trang người đàn ông.
Anoop Toffy

Tôi cũng đã đọc watchhướng dẫn sau bình luận đó và thấy rằng 0,01 giây (không phải 0,1 giây) là một con số không thực tế vì tốc độ làm mới của hầu hết các màn hình PC chỉ là 60Hz và điều này không trả lời câu hỏi theo bất kỳ cách nào. OP đã hỏi về "Số lượng tệp Linux nhanh cho một số lượng lớn tệp". Bạn cũng không đọc bất kỳ câu trả lời có sẵn nào trước khi đăng
phuclv

Tôi đã đọc câu trả lời. Nhưng những gì tôi đã đăng là một cách theo dõi thay đổi số lượng tệp trong một thư mục. ví dụ: trong khi sao chép tệp từ vị trí này sang vị trí khác, số lượng tệp sẽ thay đổi. với phương pháp tôi poster người ta có thể theo dõi điều đó. Tôi đồng ý rằng bài đăng tôi đã thực hiện không sửa đổi hoặc cải thiện bất kỳ bài viết trước.
Anoop Toffy

-2

10 giám đốc đầu tiên với số lượng lớn nhất không có tập tin.

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

3
Điều này chắc chắn trông giống một cách đáng kinh ngạc với câu trả lời (với cùng một lỗi) được viết bởi mightybs . Nếu bạn sẽ gia hạn hoặc sửa đổi mã được viết bởi người khác, ghi có chúng là phù hợp. Hiểu mã bạn đang sử dụng trong câu trả lời của bạn đủ để xác định và sửa lỗi của nó thậm chí còn phù hợp hơn .
Charles Duffy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.