Ls komutu çok sayıda dosya içeren bir dizin için çalışmıyor


70

Yaklaşık 5 milyon dosya içeren bir rehberim vardı . lsKomutu bu dizinin içinden çalıştırmaya çalıştığımda , sistemim çok miktarda bellek kullandı ve bir süre sonra kapattı. lsKomutu kullanmaktan başka dosyaları listelemek için etkili bir yol var mı ?


11
lsBu kullanımlar için bir takma adınızın olmadığından --colorveya -Fbunun lstat(2)her bir dosya için a yapmak anlamına geldiğinden emin olun .
Stéphane Chazelas

4
Bu arada, milyonlarca dosyayı tek bir dizinde depolamak oldukça kötü bir fikirdir. Dizin düzenini kontrol ederseniz, belki de bazı kriterlere göre bölebilir misiniz?
d33tah

Bu saf bir lsçağrı mıydı yoksa seçenekleri mi kullandın?
Hauke,

1
@ d33tah Evet, 5 milyon çok fazla! Kök dosya sistemimin limiti 7 milyondur.
Mikel

7
5 milyon ürün çıktısı -bu şeye bakıyorsunuz - basit liste görmek için çok fazla - peki girişi ne için istiyorsunuz?
user151019

Yanıtlar:


66

Aşağıdakileri kullanarak sıralama yapmaktan kaçının:

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

Veya eşdeğer olarak:

ls -U

10
Sütun mizanpajının ne kadar ek eklediğini merak ediyorum. -1Bayrak eklemek yardımcı olabilir.
Mikel

Muhtemelen çok değil, ama her küçük yardımcı olur, değil mi? :)
Mikel

1
@Mikel Bu sadece bir tahmin mi, yoksa bunu ölçtün mü? Bana göre -1daha uzun sürüyor.
Hauke,

10
"-1" biraz yardımcı olur. "ls -f -1" herhangi bir stat çağrısından kaçınır ve hemen her şeyi basar. Sütun çıkışı (bir terminale gönderirken varsayılan olan), her şeyi önce arabelleğe almasını sağlar. Sistemimde 8 milyon dosya içeren bir dizinde btrfs kullanımı ("seq 1 8000000 | xargs touch" tarafından yaratıldığı gibi), "time ls -f -1 | wc -l" süresi 5 saniyenin altında, "time ls -f -C | wc -l "30 saniye sürer.
Scott Lamb,

1
@ToolmakerSteve Varsayılan davranış ( -Cstdout bir terminal -1olduğunda , bir boru olduğunda) kafa karıştırıcıdır. Deneme ve ölçüm yaparken, çıktıyı görmek (komutun beklediğiniz şeyi yapmasını sağlamak için) ve bastırmak arasında geçiş yapın (terminal uygulamasının verimini şaşırtıcı faktörden kaçınmak için). Daha yüzden açıkça aracılığıyla çıktı biçimini tanımlamak, her iki modda da aynı şekilde davranması komutlarını kullanmak için -1, -C, -lvb
Scott Lamb

47

lsaslında dosyaları sıralar ve bir dizinde bir milyondan fazla dosyayı listelemeye çalışıyorsak, bu büyük bir yük haline gelir. Bu linkte de belirtildiği gibi , dosyaları kullanabiliriz straceveya kullanabiliriz find. Ancak, bu seçenekler de 5 milyon dosyama sahip olduğum için sorunum için uygun değildi. Googling bazı Biz bunu biraz kullandığımız dizinleri listelemek eğer bulundu getdents(), bunun nedeni, daha hızlı olması gerekiyordu ls, findve Pythonkütüphaneler kullanmak readdir()yavaştır fakat kullandığı getdents()altında.

Biz kullanarak dosyaları listelemek için C kodu bulabilirsiniz getdents()dan burada :

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

Yukarıdaki C programını, dosyaların listelenmesi gereken dizine kopyalayın. Ardından aşağıdaki komutları yürütün.

gcc  getdents.c -o getdents
./getdents

Zamanlama örneği : sistem yapılandırmasına bağlı olarak, getdentsçok daha hızlı olabilir ls -f. Hesaplama kümesindeki NFS bağlacı üzerinde yaklaşık 500k dosya içeren bir dizini listelemek için 40x hız artışı gösteren bazı zamanlamalar. Her komut ilk önce hemen getdentsardından 10 kez çalıştırıldı ls -f. İlk çalıştırma, muhtemelen NFS önbellek sayfası hataları nedeniyle, diğerlerinden önemli ölçüde daha yavaştır. (Bir kenara: bu bağın üzerinden d_type, birçok dosya "bilinmeyen" tür olarak göründüğü için , alan güvenilir değildir.)

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
Davanızın görüntülendiği zamanlamada küçük bir kriter ekleyebilir misiniz ls?
Bernhard

1
Tatlı. Ayrıca, adlarını listelemek yerine sadece girişleri (dosyaları) saymak için bir seçenek ekleyebilirsiniz (bu listeleme için milyonlarca çağrıyı printf'e kaydeder).
ChuckCottrill

29
casey

1
@casey Zorunda değilsiniz. Tüm bu konuşmalar getdentsvs vs readdirnoktası özlüyor.
Mikel

9
Haydi! İçeride zaten 5 milyon dosya var. Özel "ls" programınızı başka bir dizine yerleştirin.
Johan

12

O neden yavaş olduğunu en olası nedeni dosya türü boyama, sen bu önleyebilirsiniz \lsveya /bin/lsrenk seçeneklerini kapatarak.

Bir dizinde gerçekten çok fazla dosya varsa, findbunun yerine kullanmak da iyi bir seçenektir.


7
Bunun düşürülmesi gerektiğini düşünmüyorum. Sıralama bir sorundur, ancak sıralama yapılmasa bile, her dosyada ls -U --colorolacağı için uzun zaman alacaktır stat. Yani her ikisi de doğru.
Mikel

Renklendirmeyi kapatmanın performansı üzerinde büyük bir etkisi vardır lsve pek çok varsayılanda varsayılan olarak diğer adı almıştır .bashrc.
Victor Schröder

Yup /bin/ls -UÇok uzun zamandır beklediğimde çok kısa sürede a yaptım ve çıktı aldım
khebbie

-3

Bunun echo *çok daha hızlı çalıştığını biliyorum . YMMV.


4
Kabuk sıralayacaktır *. Yani bu yol muhtemelen 5 milyon dosya için hala çok yavaş.
Mikel

3
@Mikel Bundan da öte, 5 milyon dosyanın kürenin tamamen kırılacağı nokta üzerinde olduğundan eminim.
Evilsoup

4
Minimum dosya adı uzunluğu (5 milyon dosya için) 3 karakterdir (daha genel karakterlere bağlı kalırsanız belki 4) artı sınırlayıcılar = dosya başına 4 karakter, yani 20 MB komut argümanı. Bu ortak 2 MB genişletilmiş komut satırı uzunluğunun oldukça üzerindedir. İcra (ve hatta yerleşiklerin) sıkıştı.
Johan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.