Lệnh ls không hoạt động cho một thư mục có số lượng tệp khổng lồ


70

Tôi đã có một thư mục có khoảng 5 triệu tập tin. Khi tôi cố chạy lslệnh từ trong thư mục này, hệ thống của tôi đã tiêu tốn một lượng lớn bộ nhớ và đôi khi nó bị treo. Có cách nào hiệu quả để liệt kê các tập tin ngoài việc sử dụng lslệnh không?


11
Hãy chắc chắn rằng bạn không có bí danh cho lsviệc sử dụng --colorhoặc -Fđiều đó có nghĩa là thực hiện một lstat(2)cho mỗi tệp.
Stéphane Chazelas

4
Nhân tiện, lưu trữ hàng triệu tệp trong một thư mục là một ý tưởng khá tồi. Nếu bạn kiểm soát bố cục thư mục, có lẽ chia nó theo một số tiêu chí?
d33tah

Đó có phải là một lscuộc gọi thuần túy hay bạn đã sử dụng các tùy chọn?
Hauke ​​Laging

1
@ d33tah Vâng, 5 triệu là rất nhiều! Hệ thống tập tin gốc của tôi có giới hạn 7 triệu inodes.
Mikel

7
5 triệu mặt hàng cho đầu ra - làm thế nào bạn nhìn vào điều này - danh sách đơn giản là quá nhiều để xem - vậy bạn muốn danh sách này để làm gì?
dùng151019

Câu trả lời:


66

Tránh sắp xếp bằng cách sử dụng:

ls --sort=none # "do not sort; list entries in directory order"

Hoặc, tương đương:

ls -U

10
Tôi tự hỏi bao nhiêu chi phí bố trí cột thêm, quá. Thêm -1cờ có thể giúp đỡ.
Mikel

Có lẽ không nhiều, nhưng mỗi thứ đều có ích, phải không? :)
Mikel

1
@Mikel Đó chỉ là dự đoán, hoặc bạn đã đo lường điều đó? Đối với tôi có vẻ như -1mất nhiều thời gian hơn.
Hauke ​​Laging

10
"-1" giúp khá nhiều. "ls -f -1" sẽ tránh mọi cuộc gọi stat và in mọi thứ ngay lập tức. Đầu ra cột (là mặc định khi gửi đến một thiết bị đầu cuối) làm cho nó đệm mọi thứ trước tiên. Trên hệ thống của tôi, sử dụng btrfs trong thư mục có 8 triệu tệp (như được tạo bởi "seq 1 8000000 | xargs touch"), "thời gian ls -f -1 | wc -l" mất dưới 5 giây, trong khi "thời gian ls -f -C | wc -l "mất hơn 30 giây.
Scott Lamb

1
@ToolmakerSteve Hành vi mặc định ( -Ckhi thiết bị xuất chuẩn là thiết bị đầu cuối, -1khi đó là đường ống) gây nhầm lẫn. Khi bạn đang thử nghiệm và đo lường, bạn lật giữa việc nhìn thấy đầu ra (để đảm bảo lệnh đang làm những gì bạn mong đợi) và triệt tiêu nó (để tránh yếu tố gây nhiễu của thông lượng của ứng dụng đầu cuối). Tốt hơn để sử dụng các lệnh mà cư xử theo cách tương tự trong cả hai chế độ, vì vậy xác định một cách rõ ràng định dạng đầu ra thông qua -1, -C, -lvv
Scott Lamb

47

lsthực sự sắp xếp các tệp và cố gắng liệt kê chúng trở thành một chi phí khổng lồ nếu chúng ta đang cố gắng liệt kê hơn một triệu tệp trong một thư mục. Như đã đề cập trong liên kết này , chúng tôi có thể sử dụng stracehoặc findliệt kê các tệp. Tuy nhiên, những tùy chọn đó cũng có vẻ không khả thi đối với vấn đề của tôi vì tôi có 5 triệu tệp. Sau một chút loay hoay, tôi thấy rằng nếu chúng tôi liệt kê các thư mục sử dụng getdents(), nó được cho là nhanh hơn, bởi vì ls, findPythoncác thư viện sử dụng readdir()chậm hơn nhưng sử dụng getdents()bên dưới.

Chúng tôi có thể tìm mã C để liệt kê các tệp bằng cách sử dụng getdents()từ đây :

/*
 * List directories using getdents() because ls, find and Python libraries
 * use readdir() which is slower (but uses getdents() underneath.
 *
 * Compile with 
 * ]$ gcc  getdents.c -o getdents
 */
#define _GNU_SOURCE
#include <dirent.h>     /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct linux_dirent {
   long           d_ino;
   off_t          d_off;
   unsigned short d_reclen;
   char           d_name[];
};

#define BUF_SIZE 1024*1024*5

int
main(int argc, char *argv[])
{
   int fd, nread;
   char buf[BUF_SIZE];
   struct linux_dirent *d;
   int bpos;
   char d_type;

   fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
   if (fd == -1)
       handle_error("open");

   for ( ; ; ) {
       nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
       if (nread == -1)
           handle_error("getdents");

       if (nread == 0)
           break;

       for (bpos = 0; bpos < nread;) {
           d = (struct linux_dirent *) (buf + bpos);
           d_type = *(buf + bpos + d->d_reclen - 1);
           if( d->d_ino != 0 && d_type == DT_REG ) {
              printf("%s\n", (char *)d->d_name );
           }
           bpos += d->d_reclen;
       }
   }

   exit(EXIT_SUCCESS);
}

Sao chép chương trình C ở trên vào thư mục chứa các tệp cần được liệt kê. Sau đó thực hiện các lệnh dưới đây.

gcc  getdents.c -o getdents
./getdents

Ví dụ về thời gian : getdentscó thể nhanh hơn nhiều ls -f, tùy thuộc vào cấu hình hệ thống. Dưới đây là một số thời gian biểu thị tốc độ tăng 40 lần để liệt kê một thư mục chứa khoảng 500k tệp trên giá trị NFS trong cụm tính toán. Mỗi lệnh được chạy 10 lần liên tiếp, đầu tiên getdents, sau đó ls -f. Lần chạy đầu tiên chậm hơn đáng kể so với tất cả những lần khác, có thể là do lỗi trang bộ nhớ đệm NFS. (Ngoài ra: trên giá trị này, d_typetrường không đáng tin cậy, theo nghĩa là nhiều tệp xuất hiện dưới dạng "không xác định".)

command: getdents $bigdir
usr:0.08 sys:0.96  wall:280.79 CPU:0%
usr:0.06 sys:0.18  wall:0.25   CPU:97%
usr:0.05 sys:0.16  wall:0.21   CPU:99%
usr:0.04 sys:0.18  wall:0.23   CPU:98%
usr:0.05 sys:0.20  wall:0.26   CPU:99%
usr:0.04 sys:0.18  wall:0.22   CPU:99%
usr:0.04 sys:0.17  wall:0.22   CPU:99%
usr:0.04 sys:0.20  wall:0.25   CPU:99%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
command: /bin/ls -f $bigdir
usr:0.53 sys:8.39  wall:8.97   CPU:99%
usr:0.53 sys:7.65  wall:8.20   CPU:99%
usr:0.44 sys:7.91  wall:8.36   CPU:99%
usr:0.50 sys:8.00  wall:8.51   CPU:100%
usr:0.41 sys:7.73  wall:8.15   CPU:99%
usr:0.47 sys:8.84  wall:9.32   CPU:99%
usr:0.57 sys:9.78  wall:10.36  CPU:99%
usr:0.53 sys:10.75 wall:11.29  CPU:99%
usr:0.46 sys:8.76  wall:9.25   CPU:99%
usr:0.50 sys:8.58  wall:9.13   CPU:99%

14
Bạn có thể thêm một điểm chuẩn nhỏ trong thời gian mà trường hợp của bạn không hiển thị với ls?
Bernhard

1
Ngọt. Và bạn có thể thêm một tùy chọn để chỉ cần đếm các mục (tệp) thay vì liệt kê tên của chúng (lưu hàng triệu cuộc gọi vào printf, cho danh sách này).
ChuckCottrill 17/03/2016

29
Bạn biết thư mục của bạn quá lớn khi bạn phải viết mã tùy chỉnh để liệt kê nội dung của nó ...
casey

1
@casey Ngoại trừ bạn không phải. Tất cả điều này nói về getdentsvs readdirbỏ lỡ điểm.
Mikel

9
Nào! Nó đã có 5 triệu tệp trong đó. Đặt chương trình "ls" tùy chỉnh của bạn vào một số thư mục khác.
Johan

12

Lý do rất có thể khiến nó chậm là màu tập tin, bạn có thể tránh điều này bằng \lshoặc /bin/lstắt các tùy chọn màu.

Nếu bạn thực sự có rất nhiều tệp trong một thư mục, sử dụng findthay thế cũng là một lựa chọn tốt.


7
Tôi không nghĩ rằng điều này nên đã bị hạ thấp. Sắp xếp là một vấn đề, nhưng ngay cả khi không sắp xếp, ls -U --colorsẽ mất nhiều thời gian vì statmỗi tệp sẽ như vậy . Vậy là cả hai đều đúng.
Mikel

Tắt màu có ảnh hưởng rất lớn đến hiệu suất lsvà nó được đặt bí danh theo mặc định trong nhiều .bashrcs ngoài đó.
Victor Schröder

Yup tôi đã làm một /bin/ls -Uvà có đầu ra ngay lập tức, so với việc chờ đợi trong một thời gian rất dài trước đó
khebbie

-3

Tôi thấy rằng echo *hoạt động nhanh hơn nhiều so với ls. YMMV.


4
Vỏ sẽ sắp xếp *. Vì vậy, cách này có lẽ vẫn rất chậm đối với 5 triệu tệp.
Mikel

3
@Mikel Hơn thế nữa, tôi khá chắc chắn rằng 5 triệu tệp vượt quá điểm mà toàn cầu sẽ bị phá vỡ hoàn toàn.
evilsoup 18/03/14

4
Độ dài tên tệp tối thiểu (cho 5 triệu tệp) là 3 ký tự (có thể là 4 nếu bạn dính vào các ký tự phổ biến hơn) cộng với dấu phân cách = 4 ký tự trên mỗi tệp, tức là 20 MB đối số lệnh. Đó là tốt hơn so với chiều dài dòng lệnh mở rộng 2MB phổ biến. Exec (và thậm chí cả các nội trang) sẽ bực bội.
Johan
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.