Perintah ls tidak berfungsi untuk direktori dengan sejumlah besar file


70

Saya memiliki direktori yang memiliki sekitar 5 juta file. Ketika saya mencoba menjalankan lsperintah dari dalam direktori ini, sistem saya menghabiskan banyak sekali memori dan hang setelah beberapa saat. Apakah ada cara efisien untuk mendaftar file selain menggunakan lsperintah?


11
Pastikan Anda tidak memiliki alias untuk lspenggunaan itu --coloratau -Fkarena artinya melakukan lstat(2)untuk setiap file.
Stéphane Chazelas

4
Ngomong-ngomong, menyimpan jutaan file dalam satu direktori adalah ide yang agak buruk. Jika Anda mengontrol tata letak direktori, mungkin membaginya dengan beberapa kriteria?
d33tah

Apakah itu lspanggilan murni atau apakah Anda menggunakan opsi?
Hauke ​​Laging

1
@ d33tah Ya, 5 juta banyak! Sistem file root saya memiliki batas 7 juta inode.
Mikel

7
5 juta item untuk ditampilkan - bagaimana Anda melihat ini - daftar sederhana terlalu banyak untuk dilihat - jadi Anda ingin daftar apa?
user151019

Jawaban:


66

Hindari penyortiran dengan menggunakan:

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

Atau, yang setara:

ls -U

10
Saya bertanya-tanya berapa banyak overhead yang menambahkan tata letak kolom juga. Menambahkan -1bendera dapat membantu.
Mikel

Mungkin tidak banyak, tetapi setiap sedikit membantu, bukan? :)
Mikel

1
@Mikel Apakah itu hanya dugaan, atau sudahkah Anda mengukurnya? Bagi saya tampaknya itu -1membutuhkan waktu lebih lama.
Hauke ​​Laging

10
"-1" sedikit membantu. "ls -f -1" akan menghindari panggilan stat dan segera mencetak semuanya. Output kolom (yang merupakan default saat mengirim ke terminal) membuatnya buffer terlebih dahulu. Di sistem saya, menggunakan btrfs dalam direktori dengan 8 juta file (seperti yang dibuat oleh "seq 1 8000000 | xargs touch"), "waktu ls -f -1 | wc -l" membutuhkan waktu di bawah 5 detik, sedangkan "waktu ls -f -C | wc -l "membutuhkan waktu lebih dari 30 detik.
Scott Lamb

1
@ToolmakerSteve Perilaku default ( -Cketika stdout adalah terminal, -1ketika itu pipa) membingungkan. Saat Anda bereksperimen dan mengukur, Anda beralih antara melihat output (untuk memastikan perintah melakukan apa yang Anda harapkan) dan menekannya (untuk menghindari faktor perancu dari throughput aplikasi terminal). Lebih baik menggunakan perintah yang berperilaku dengan cara yang sama di kedua mode, sehingga secara eksplisit menentukan format output melalui -1, -C, -l, dll
Scott Lamb

47

lssebenarnya mengurutkan file dan mencoba untuk membuat daftar yang menjadi overhead besar jika kita mencoba mendaftar lebih dari satu juta file di dalam direktori. Seperti disebutkan dalam tautan ini , kita dapat menggunakan straceatau finduntuk membuat daftar file. Namun, opsi-opsi itu juga tampak tidak mungkin untuk masalah saya karena saya memiliki 5 juta file. Setelah sedikit googling, saya menemukan bahwa jika kita daftar direktori menggunakan getdents(), itu seharusnya lebih cepat, karena ls, finddan Pythonperpustakaan menggunakan readdir()yang lebih lambat tetapi menggunakan di getdents()bawahnya.

Kita dapat menemukan kode C untuk membuat daftar file yang digunakan getdents()dari sini :

/*
 * 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);
}

Salin program C di atas ke dalam direktori di mana file harus terdaftar. Kemudian jalankan perintah di bawah ini.

gcc  getdents.c -o getdents
./getdents

Contoh waktu : getdentsbisa lebih cepat daripada ls -f, tergantung pada konfigurasi sistem. Berikut adalah beberapa pengaturan waktu yang menunjukkan peningkatan kecepatan 40x untuk daftar direktori yang berisi sekitar 500k file di atas mount NFS di cluster komputasi. Setiap perintah dijalankan 10 kali berturut-turut, pertama getdents, lalu ls -f. Jalankan pertama secara signifikan lebih lambat dari yang lain, mungkin karena kesalahan halaman caching NFS. (Selain: di atas mount ini, d_typebidang ini tidak dapat diandalkan, dalam arti bahwa banyak file muncul sebagai tipe "tidak dikenal".)

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
Bisakah Anda menambahkan patokan kecil dalam hal waktu kasing Anda ditampilkan ls?
Bernhard

1
Manis. Dan Anda dapat menambahkan opsi untuk hanya menghitung entri (file) daripada mendaftarkan nama mereka (menyimpan jutaan panggilan ke printf, untuk listing ini).
ChuckCottrill

29
Anda tahu direktori Anda terlalu besar ketika Anda harus menulis kode khusus untuk mencantumkan isinya ...
casey

1
@casey Kecuali Anda tidak harus. Semua pembicaraan tentang getdentsvs ini tidak readdirtepat.
Mikel

9
Ayolah! Sudah ada 5 juta file di sana. Masukkan program "ls" khusus Anda ke direktori lain.
Johan

12

Alasan yang paling mungkin mengapa lambat adalah pewarnaan jenis file, Anda dapat menghindari ini dengan \lsatau /bin/lsmematikan opsi warna.

Jika Anda benar-benar memiliki begitu banyak file dalam suatu dir, menggunakan findgantinya juga merupakan pilihan yang baik.


7
Saya tidak berpikir ini seharusnya diturunkan. Penyortiran adalah satu masalah, tetapi bahkan tanpa penyortiran, ls -U --colorakan memakan waktu lama karena akan statsetiap file. Jadi keduanya benar.
Mikel

Mematikan pewarnaan memiliki dampak besar pada kinerja lsdan itu secara default di banyak banyak .bashrcdi luar sana.
Victor Schröder

Yup, saya melakukan /bin/ls -Udan mendapatkan hasil dalam waktu singkat, dibandingkan dengan menunggu sangat lama sebelumnya
khebbie

-3

Saya menemukan bahwa echo *bekerja jauh lebih cepat daripada ls. YMMV.


4
Shell akan mengurutkan *. Jadi cara ini mungkin masih sangat lambat untuk 5 juta file.
Mikel

3
@ Kemungkinan Lebih dari itu, saya cukup yakin bahwa 5 juta file melebihi titik di mana globbing akan hancur total.
evilsoup

4
Panjang nama file minimum (untuk 5 juta file) adalah 3 karakter (mungkin 4 jika Anda tetap menggunakan karakter yang lebih umum) ditambah pembatas = 4 karakter per file, yaitu 20 MB argumen perintah. Itu jauh melebihi panjang perintah umum 2MB yang diperluas. Exec (dan bahkan builtin) akan mogok.
Johan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.