बड़ी संख्या में फ़ाइलों के लिए फास्ट लिनक्स फ़ाइल गणना


137

मैं एक विशेष निर्देशिका में फ़ाइलों की संख्या को खोजने के लिए सबसे अच्छा तरीका जानने की कोशिश कर रहा हूं जब बहुत बड़ी संख्या में फाइलें (> 100,000) हैं।

जब ऐसी कई फाइलें होती हैं, तो निष्पादित करने ls | wc -lमें काफी लंबा समय लगता है। मेरा मानना ​​है कि यह इसलिए है क्योंकि यह सभी फाइलों के नाम लौटा रहा है। मैं संभव के रूप में डिस्क IO के रूप में कम लेने की कोशिश कर रहा हूँ।

मैंने कुछ लाभ उठाने के लिए कुछ शेल और पर्ल स्क्रिप्ट के साथ प्रयोग किया है। कोई विचार?


2
सुनिश्चित करें कि आपका "ls" / usr / bin / ls है न कि किसी प्रशंसक का उपनाम।
ग्लेन जैकमैन

ऐसे ही दिलचस्प सवालों के जवाब यहाँ: serverfault.com/questions/205071/…
नं

यह इंगित करने के लायक है कि सबसे ज्यादा अगर इस सवाल को प्रस्तुत सभी समाधान लिनक्स के लिए विशिष्ट नहीं हैं , लेकिन सभी * एनआईएक्स-जैसे सिस्टम के लिए बहुत सामान्य हैं। शायद "लिनक्स" टैग को हटाना उचित है।
क्रिस्टोफर शुल्त्स

जवाबों:


189

डिफ़ॉल्ट lsरूप से नामों को, जो कि बहुत सारे होने पर कुछ समय ले सकते हैं। जब तक सभी नामों को पढ़ा और क्रमबद्ध नहीं किया जाता है, तब तक कोई आउटपुट नहीं होगा। ls -fछँटाई बंद करने के लिए विकल्प का उपयोग करें ।

ls -f | wc -l

ध्यान दें कि यह भी सक्षम हो जाएगा -a, इसलिए ., ..के साथ शुरू, और अन्य फ़ाइलों .में गिना जाएगा।


11
+1 और मुझे लगा कि मुझे पता है कि वहाँ सब कुछ पता था ls
भीड़

5
ZOMG। 100K लाइनों की छंटाई कुछ भी नहीं है - हर फाइल पर stat()कॉल की तुलना में ls। इस प्रकार यह तेजी से काम findनहीं stat()करता है।
डमी ००००००१

12
ls -fstat()या तो नहीं है । लेकिन निश्चित रूप से दोनों lsऔर findकॉल stat()जब कुछ विकल्पों का उपयोग किया जाता है, जैसे कि ls -lया find -mtime
निशान 4o

7
संदर्भ के लिए, छोटे-ईश स्लाइसहोस्ट बॉक्स पर 2.5 मिलियन jpgs को गिनने में 1-2 मिनट लगते हैं।
दार्शनिको

6
यदि आप गिनती में उपनिर्देशिका जोड़ना चाहते हैं, तो करेंls -fR | wc -l
Ryan Walls

62

सबसे तेज़ तरीका एक उद्देश्य-निर्मित कार्यक्रम है, जैसे:

#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उपरोक्त स्रोत से संकलित कार्यक्रम है।

EDIT 2016-09-26

लोकप्रिय मांग के कारण, मैंने इस कार्यक्रम को पुनरावर्ती होने के लिए फिर से लिखा है, इसलिए यह उपनिर्देशिकाओं में छोड़ देगा और फ़ाइलों और निर्देशिकाओं को अलग-अलग गिनना जारी रखेगा।

चूंकि यह स्पष्ट है कि कुछ लोग यह जानना चाहते हैं कि यह सब कैसे करना है, इसलिए मुझे यह स्पष्ट करने के लिए कोड में बहुत सारी टिप्पणियाँ हैं कि यह क्या हो रहा है। मैंने इसे लिखा और 64-बिट लिनक्स पर इसका परीक्षण किया, लेकिन इसे माइक्रोसॉफ्ट विंडोज सहित किसी भी POSIX-compliant सिस्टम पर काम करना चाहिए । बग रिपोर्ट का स्वागत है; यदि आप इसे अपने AIX या OS / 400 या जो भी काम नहीं कर सकते हैं, तो मैं इसे अपडेट करके खुश हूं।

जैसा कि आप देख सकते हैं, यह मूल की तुलना में बहुत अधिक जटिल है और आवश्यक रूप से ऐसा है: कम से कम एक फ़ंक्शन को पुनरावर्ती रूप से कहा जाना चाहिए जब तक कि आप कोड बहुत जटिल नहीं बनना चाहते (जैसे कि एक उपनिर्देशिका स्टैक का प्रबंधन और एक ही लूप में प्रसंस्करण)। चूंकि हमें फ़ाइल प्रकारों की जांच करनी है, विभिन्न ओएस, मानक पुस्तकालयों, आदि के बीच अंतर खेलने में आता है, इसलिए मैंने एक कार्यक्रम लिखा है जो किसी भी प्रणाली पर प्रयोग करने योग्य होने की कोशिश करता है जहां यह संकलित होगा।

जाँच में बहुत कम त्रुटि है, और countफ़ंक्शन स्वयं त्रुटियों की रिपोर्ट नहीं करता है। केवल वही कॉल जो वास्तव में विफल हो सकते हैं opendirऔर stat(यदि आप भाग्यशाली नहीं हैं और एक सिस्टम है direntजिसमें फ़ाइल प्रकार पहले से मौजूद है)। मैं उप-पथ पथनाम की कुल लंबाई की जाँच करने के बारे में पागल नहीं हूं, लेकिन सैद्धांतिक रूप से, सिस्टम को किसी भी पथ के नाम की अनुमति नहीं देनी चाहिए जो उससे अधिक लंबा है PATH_MAX। यदि चिंताएं हैं, तो मैं इसे ठीक कर सकता हूं, लेकिन यह सिर्फ और अधिक कोड है जिसे सी लिखने के लिए सीखने वाले किसी व्यक्ति को समझाया जाना चाहिए। इस कार्यक्रम का एक उदाहरण है कि कैसे उपनिर्देशिका में पुनरावृत्ति करना है।

#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

मैंने @FlyingCodeMonkey द्वारा सुझाए गए दो परिवर्तनों को शामिल किया है:

  1. के lstatबजाय का उपयोग करें stat। यह प्रोग्राम के व्यवहार को बदल देगा यदि आपके पास जिस निर्देशिका में आप स्कैन कर रहे हैं उसमें सहानुभूति निर्देशिका है। पिछला व्यवहार यह था कि (लिंक किया हुआ) उपनिर्देशिका में इसकी फ़ाइल गणना समग्र गणना में जोड़ी जाएगी; नया व्यवहार यह है कि लिंक की गई निर्देशिका एक ही फ़ाइल के रूप में गिनेगी, और इसकी सामग्री को नहीं गिना जाएगा।
  2. यदि किसी फ़ाइल का पथ बहुत लंबा है, तो एक त्रुटि संदेश उत्सर्जित हो जाएगा और प्रोग्राम बंद हो जाएगा।

EDIT 2017-06-29

किसी भी भाग्य के साथ, यह इस उत्तर का अंतिम संपादन होगा :)

मैंने इस कोड को एक GitHub रिपॉजिटरी में कॉपी कर लिया है ताकि कोड प्राप्त करने के लिए इसे थोड़ा आसान बनाया जा सके (कॉपी / पेस्ट के बजाय, आप बस स्रोत डाउनलोड कर सकते हैं ), और इसके अलावा किसी को भी एक पुल सबमिट करके संशोधन का सुझाव देना आसान हो जाता है GitHub से -request।

स्रोत अपाचे लाइसेंस 2.0 के तहत उपलब्ध है। पैच * आपका स्वागत है!


  • "पैच" वह है जो मेरे जैसे पुराने लोग "पुल अनुरोध" कहते हैं।

2
सिर्फ महान! धन्यवाद! और उन अनजान लोगों के लिए: आप टर्मिनल में उपरोक्त कोड की gcc -o dircnt dircnt.c./dircnt some_dir
शिकायत

क्या इस पुनरावर्ती को बनाने का एक आसान तरीका है?
ck_

@ck_ ज़रूर, यह आसानी से पुनरावर्ती बनाया जा सकता है। क्या आपको समाधान के साथ मदद की ज़रूरत है, या क्या आप चाहते हैं कि मैं पूरी बात लिखूं?
क्रिस्टोफर शुल्त्स

1
@ChristopherSchultz, आपके द्वारा ऊपर दिए गए बेंचमार्क - प्रश्न में निर्देशिका कितनी बड़ी थी?
डोम विनयार्ड

1
मैं वास्तव में पायथन में इसका उपयोग करना चाहता था इसलिए मैंने इसे ffcount पैकेज के रूप में पैक किया । कोड @ChristopherSchultz उपलब्ध कराने के लिए धन्यवाद!
GjjvdBurg

35

क्या आपने खोजने की कोशिश की? उदाहरण के लिए:

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

1
यह वर्तमान निर्देशिका के अंतर्गत फ़ाइलों को पुन: खोजेगा
mark4o

मेरे सिस्टम पर, find /usr/share | wc -l(~ 137,000 फाइलें) ls -R /usr/share | wc -lप्रत्येक के पहले रन पर (डायर नाम, dir योग और रिक्त लाइनों सहित ~ 160,000 लाइनों ) की तुलना में लगभग 25% तेज है और बाद में (कैश्ड) रन की तुलना करते हुए कम से कम दो बार तेजी से।
अगली सूचना तक रोक दिया गया।

11
यदि वह केवल वर्तमान निर्देशिका चाहता है, न कि पूरे पेड़ को पुनरावर्ती रूप से, वह -maxdepth को खोजने के लिए 1 विकल्प जोड़ सकता है।
igustin

3
ऐसा लगता है कि इसका कारण यह है कि आप इसका उपयोग कैसे कर रहे हैं की findतुलना में तेज है । यदि आप छँटाई बंद कर देते हैं, और समान प्रदर्शन करते हैं। lslslsfind
क्रिस्टोफर शुल्त्स

17

40 000 फाइलों के खिलाफ जांच, ls और perl का परीक्षण करें: एक ही गति (हालांकि मैंने कैश को साफ़ करने की कोशिश नहीं की थी):

[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

और पर्ल ओपेंडिर / रीडिर के साथ, एक ही समय:

[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

ध्यान दें: मैंने / बिन / ls -f का उपयोग किया है ताकि उपनाम आदेश को बायपास कर सकें जो फ़ाइल ऑर्डर से बचने के लिए थोड़ा और -f धीमा हो सकता है । ls के बिना -f दुगने / perl की तुलना में दोगुना धीमा है, अगर ls को -f के साथ प्रयोग किया जाए, तो ऐसा लगता है:

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

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

मैं यह भी चाहता हूं कि सभी अनावश्यक सूचनाओं के बिना फाइल सिस्टम को सीधे पूछने के लिए कुछ स्क्रिप्ट होनी चाहिए।

पीटर वान डेर हाइजेन, ग्लेन जैकमैन और mark4o के उत्तर पर आधारित परीक्षण।

थॉमस


5
आपको परीक्षणों के बीच कैश को निश्चित रूप से साफ़ करना चाहिए। पहली बार जब मैं ls -l | wc -l1M फ़ाइलों के साथ बाहरी 2.5 "HDD पर एक फ़ोल्डर में चलाता हूं , तो ऑपरेशन को समाप्त करने के लिए लगभग 3 मिनट लगते हैं। दूसरी बार 12 सेकंड IIRC लगते हैं। इसके अलावा यह संभवतः आपकी फ़ाइल प्रणाली पर भी निर्भर कर सकता है।" प्रयोग कर रहा था Btrfs
बेहरंग सईदज़ादेह

धन्यवाद, पर्ल स्निपेट मेरे लिए समाधान है। $ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
पैजआउट

5

आप अपनी आवश्यकताओं के आधार पर आउटपुट को बदल सकते हैं, लेकिन यहां एक बैश वन-लाइनर है जिसे मैंने पुनरावर्ती रूप से गिनने और संख्यात्मक रूप से नामित निर्देशिकाओं की एक श्रृंखला में फ़ाइलों की संख्या की रिपोर्ट करने के लिए लिखा है।

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

यह दी गई निर्देशिका में सभी फ़ाइलों (निर्देशिकाओं) के लिए पुनरावर्ती रूप से दिखता है और एक हैश जैसे प्रारूप में परिणाम देता है। सरल कमांड को खोजने के लिए ट्विक्स यह बता सकता है कि आप किस प्रकार की फ़ाइलों को अधिक विशिष्ट गिनना चाहते हैं, आदि।

कुछ इस तरह से परिणाम:

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

1
मैंने उदाहरण को थोड़ा भ्रमित किया। मैं सोच रहा था कि निर्देशिका नामों के बजाय बाईं ओर संख्याएं क्यों थीं। हालांकि इसके लिए धन्यवाद, मैंने कुछ मामूली ट्विक्स के साथ इसका उपयोग किया। (निर्देशिकाओं की गिनती और बेस फोल्डर का नाम छोड़ना। i for $ in (ls -1 | सॉर्ट -n); {echo "$ i => $ ($ {i} | wc -l) खोजें;};}
TheJacobTorlor

बाईं ओर की संख्या मेरे उदाहरण डेटा से मेरी निर्देशिका के नाम हैं। क्षमा करें कि भ्रमित था।
मई 19914

1
ls -1 ${dir}अधिक रिक्त स्थान के बिना ठीक से काम नहीं करेगा। इसके अलावा, इस बात की कोई गारंटी नहीं है कि नाम वापस lsकर दिया जा सकता है find, क्योंकि lsमानव उपभोग के लिए गैर-मुद्रण योग्य वर्ण से बच जाता है। ( mkdir $'oddly\nnamed\ndirectory'यदि आप एक विशेष रूप से दिलचस्प परीक्षण मामला चाहते हैं)। देखें कि आपको ls (1) के आउटपुट को पार्स क्यों नहीं करना चाहिए
चार्ल्स डफी

4

मेरे लिए हैरानी की बात है, नंगे-हड्डियों का पता ls -f से बहुत अधिक तुलनीय है

> time ls -f my_dir | wc -l
17626

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

बनाम

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

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

बेशक, तीसरे दशमलव स्थान पर मूल्य हर बार जब आप इनमें से किसी को भी निष्पादित करते हैं, तो वे लगभग समान होते हैं। ध्यान दें कि findएक अतिरिक्त इकाई लौटाता है, क्योंकि यह वास्तविक निर्देशिका को स्वयं गिनता है (और, जैसा कि पहले उल्लेख किया गया है, ls -fदो अतिरिक्त इकाइयों को लौटाता है, क्योंकि यह भी मायने रखता है और ..)।


4

बस इसे पूर्णता के लिए जोड़ रहा है। पाठ्यक्रम का सही उत्तर पहले से ही किसी और द्वारा पोस्ट किया गया है, लेकिन आप ट्री प्रोग्राम के साथ फाइलों और निर्देशिकाओं की गिनती भी प्राप्त कर सकते हैं।

tree | tail -n 1अंतिम पंक्ति प्राप्त करने के लिए कमांड चलाएं , जो "763 निर्देशिकाओं, 9290 फ़ाइलों" की तरह कुछ कहेगा। यह छिपी हुई फ़ाइलों को छोड़कर, फ़ाइलों और फ़ोल्डरों को पुन: गणना करता है, जिसे ध्वज के साथ जोड़ा जा सकता है -a। संदर्भ के लिए, मेरे कंप्यूटर पर 4.8 सेकंड का समय लगा, वृक्ष के लिए मेरे पूरे घर को गिनने के लिए, जो कि 24777 निर्देशिकाएं, 238680 फाइलें थीं। find -type f | wc -l5.3 सेकंड, आधा सेकंड लंबा समय लिया, इसलिए मुझे लगता है कि पेड़ बहुत प्रतिस्पर्धी गति-वार है।

जब तक आपके पास कोई सबफ़ोल्डर नहीं है, तब तक फ़ाइलों को गिनने के लिए पेड़ एक त्वरित और आसान तरीका है।

इसके अलावा, और विशुद्ध रूप से इसके मज़े के लिए, आप tree | grep '^├'केवल वर्तमान निर्देशिका में फ़ाइलों / फ़ोल्डरों को दिखाने के लिए उपयोग कर सकते हैं - यह मूल रूप से बहुत धीमा संस्करण है ls


Brew install tailओएस एक्स के लिए
द अनफिन कैट

@ TheUnfunCat tailको पहले से ही आपके Mac OS X सिस्टम पर स्थापित किया जाना चाहिए।
क्रिस्टोफर शुल्त्स

4

फास्ट लिनक्स फ़ाइल गणना

सबसे तेज लिनक्स फाइल काउंट मुझे पता है

locate -c -r '/home'

नहीं है कोई ग्रेप आह्वान करने के लिए की जरूरत है! लेकिन जैसा कि उल्लेख किया गया है कि आपके पास एक नया डेटाबेस होना चाहिए (क्रोन जॉब द्वारा दैनिक अद्यतन किया जाता है, या मैन्युअल रूप से sudo updatedb)।

से आदमी का पता लगाने

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

अतिरिक्त आपको पता होना चाहिए कि यह निर्देशिका को फ़ाइलों के रूप में भी गिना जाता है!


BTW: यदि आप अपने सिस्टम प्रकार पर अपनी फ़ाइलों और निर्देशिकाओं का अवलोकन चाहते हैं

locate -S

यह निर्देशिकाओं, फाइलों आदि की संख्या को आउटपुट करता है।


ध्यान दें कि आपको यह सुनिश्चित करना है कि डेटाबेस अप-टू-डेट है
phuclv

1
LOL यदि आपके पास पहले से ही एक डेटाबेस में सभी मायने रखता है, तो आप निश्चित रूप से जल्दी से गिन सकते हैं। :)
क्रिस्टोफर शुल्त्स

3

यहाँ यह लिखना क्योंकि मेरे पास एक उत्तर पर टिप्पणी करने के लिए पर्याप्त प्रतिष्ठा अंक नहीं हैं , लेकिन मुझे अपना जवाब छोड़ने की अनुमति है , जिसका कोई मतलब नहीं है। वैसे भी ...

क्रिस्टोफर शुल्त्स के जवाब के बारे में , मैं सुझाव देता हूं कि स्टेट को बदलने के लिए स्टैट को बदलना और संभवतः अतिप्रवाह से बचने के लिए सीमा-जाँच जोड़ना:

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

Lstat का उपयोग करने का सुझाव सिम्बलिंक का पालन करने से बचना है, जो यदि किसी निर्देशिका में किसी पैरेंट डायरेक्टरी के लिए सिम्लिंक होता है, तो वह चक्र को जन्म दे सकता है।


2
मोडिंग करना क्योंकि lstatएक अच्छा सुझाव था और आप इसके लिए कर्म के लायक थे। यह सुझाव ऊपर और अब GitHub पर पोस्ट किए गए मेरे कोड में शामिल किया गया था।
क्रिस्टोफर

2

आप की कोशिश कर सकते यदि का उपयोग करते हुए opendir()और readdir()में Perlतेजी से होता है। उन फ़ंक्शन के एक उदाहरण के लिए यहां देखें


2
उपयोग: perl -e 'opendir D, ";"; @files = readdir D; बंद डी; प्रिंट स्केलर (@ फ़ाइल्स) '
ग्लेन जैकमैन

2

यह उत्तर इस पृष्ठ पर बहुत बड़े, बहुत नेस्टेड निर्देशिकाओं के लिए लगभग सभी चीज़ों से अधिक तेज़ है:

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

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


1
अच्छा लगा। चूंकि आपके पास पहले से ही सभी फाइलों की अप-टू-डेट डीबी है, इसलिए उस पर दोबारा जाने की जरूरत नहीं है। लेकिन दुर्भाग्य से, आपको यह सुनिश्चित करना चाहिए कि इस विधि के लिए अपडेटेड कमांड पहले ही चल चुका है और पूरा हो चुका है।
क्रिस रीड

आपको grep करने की आवश्यकता नहीं है। Abu_bua के समाधानlocate -c -r '/path' में उपयोग करें
phuclv

2

मैं ~ 10K फ़ाइलों के साथ ~ 10K फ़ोल्डर के डेटासेट में फ़ाइलों को गिनने की कोशिश कर रहा था। कई दृष्टिकोणों के साथ समस्या यह है कि वे अनुमानित रूप से 100M फाइल करते हैं, जो उम्र लेता है।

मैंने क्रिस्टोफर-शुल्त्ज़ द्वारा दृष्टिकोण का विस्तार करने के लिए स्वतंत्रता ली, ताकि यह args (उसके पुनरावर्ती दृष्टिकोण स्टेट का उपयोग करता है) के माध्यम से गुजर निर्देशिकाओं का समर्थन करता है।

निम्नलिखित को फाइल में डालें 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;
}

एक के बाद gcc -o dircnt_args dircnt_args.cआप इस तरह यह आह्वान कर सकते हैं:

dircnt_args /your/dirs/*

10K फ़ोल्डरों में 100 एम फाइलों पर उपरोक्त काफी जल्दी पूरा होता है (पहले रन के लिए 5 मिनट, कैश पर फॉलोअप: ~ 23 एस)।

एक घंटे से भी कम समय में समाप्त होने वाला एकमात्र अन्य दृष्टिकोण कैश पर लगभग 1 मिनट था ls -f /your/dirs/* | wc -l। यह गिनती प्रति वर्ष नए सिरे से कुछ हद तक बंद है ...

उम्मीद के अलावा, मेरी कोई भी कोशिश findएक घंटे के भीतर वापस नहीं आई: - /


किसी ऐसे व्यक्ति के लिए जो सी प्रोग्रामर नहीं है, क्या आप बता सकते हैं कि यह तेजी से क्यों होगा, और यह कैसे एक ही काम किए बिना एक ही उत्तर प्राप्त करने में सक्षम है?
18'18

आपको एक सी प्रोग्रामर होने की आवश्यकता नहीं है, बस यह समझें कि किसी फ़ाइल को स्टेट करने के लिए इसका क्या अर्थ है और निर्देशिकाओं का प्रतिनिधित्व कैसे किया जाता है: निर्देशिका अनिवार्य रूप से फ़ाइल नाम और इनोड्स की सूची है। यदि आप एक फ़ाइल को स्टेट करते हैं, तो आप इनोड को एक्सेस करते हैं जो उदाहरण के लिए फाइल-साइज़, अनुमतियां, ... जैसी जानकारी प्राप्त करने के लिए ड्राइव पर है। यदि आप प्रति dir प्रति काउंट में रुचि रखते हैं, तो आपको इनकोड जानकारी तक पहुंचने की आवश्यकता नहीं है, जो आपको बहुत समय बचा सकती है।
जोर्न हीस

ओरेकल लाइनक्स पर यह सेगफॉल्ट्स, gcc संस्करण 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... सापेक्ष पथ और दूरस्थ fs इसका कारण प्रतीत होता है
रोंडो

2

लिनक्स पर सबसे तेज़ तरीका (प्रश्न को लिनक्स के रूप में चिह्नित किया गया है), सीधे सिस्टम कॉल का उपयोग करना है। यहां एक छोटा प्रोग्राम है जो एक डायरेक्टरी में फाइल्स (केवल, नो डायर) की गिनती करता है। आप लाखों फाइलों को गिन सकते हैं और यह क्रिस्टोफर शुल्त्स के उत्तर की तुलना में "ls -f" की तुलना में लगभग 2.5 गुना तेज और लगभग 1.3-1.5 गुना तेज है।

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

पुनश्च: यह पुनरावर्ती नहीं है लेकिन आप इसे प्राप्त करने के लिए इसे संशोधित कर सकते हैं।


1
मुझे यकीन नहीं है कि मैं सहमत हूं कि यह तेज है। मैंने वह सब कुछ ट्रेस-थ्रू नहीं किया है जो कंपाइलर opendir/ के साथ करता है readdir, लेकिन मुझे संदेह है कि यह अंत में लगभग समान कोड को उबालता है। सिस्टम कॉल करना उस तरह से भी पोर्टेबल नहीं है और, जैसा कि लिनक्स एबीआई स्थिर नहीं है, एक सिस्टम पर संकलित एक प्रोग्राम दूसरे पर ठीक से काम करने की गारंटी नहीं है (हालांकि यह किसी भी * एनआईएक्स सिस्टम आईएमओ पर स्रोत से कुछ भी संकलित करने के लिए काफी अच्छी सलाह है। )। यदि गति कुंजी है, तो यह एक अच्छा समाधान है यदि यह वास्तव में गति में सुधार करता है - मैंने अलग से कार्यक्रमों को बेंचमार्क नहीं किया है।
क्रिस्टोफर

1

lsफ़ाइलों के नामों को छांटने में अधिक समय व्यतीत होता है, -fछँटाई को अक्षम करने के लिए उपयोग करने से कुछ समय बच जाएगा:

ls -f | wc -l

या आप उपयोग कर सकते हैं find:

find . -type f | wc -l

0

मुझे एहसास हुआ कि जब आपके पास भारी मात्रा में डेटा होता है तो मेमोरी प्रोसेसिंग में उपयोग नहीं किया जाता है। इसलिए मैंने परिणाम को एक फ़ाइल में सहेजा और उसका विश्लेषण करने के बाद

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

यह सबसे तेज़ समाधान नहीं है क्योंकि हार्ड डिस्क बेहद धीमी हैं। आपके द्वारा वर्षों पहले पोस्ट किए गए अन्य कुशल तरीके हैं
phuclv

0

आपको ls / find के स्थान पर "getdents" का उपयोग करना चाहिए

यहाँ एक बहुत अच्छा लेख है जो गेटडेंट्स दृष्टिकोण का वर्णन करता है।

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

यहाँ अर्क है:

ls और व्यावहारिक रूप से एक निर्देशिका को सूचीबद्ध करने की हर दूसरी विधि (अजगर os.listdir सहित, खोजें।) libc readdir () पर निर्भर हैं। हालाँकि readdir () केवल एक समय में 32K निर्देशिका प्रविष्टियों को पढ़ता है, जिसका अर्थ है कि यदि आपके पास एक ही निर्देशिका में बहुत सारी फाइलें हैं (यानी निर्देशिका प्रविष्टियों की 500M) तो सभी निर्देशिका प्रविष्टियों को पढ़ने के लिए बहुत लंबा समय लगने वाला है , विशेष रूप से एक धीमी डिस्क पर। बड़ी संख्या में फ़ाइलों वाली निर्देशिकाओं के लिए, आपको उन टूल्स की तुलना में अधिक गहरी खुदाई करने की आवश्यकता होगी जो रीडिर () पर निर्भर हैं। आपको libc से सहायक विधियों के बजाय सीधे getdents () syscall का उपयोग करने की आवश्यकता होगी।

हम यहां से getdents () का उपयोग करके फाइलों को सूचीबद्ध करने के लिए C कोड पा सकते हैं :

दो संशोधनों के लिए आपको एक निर्देशिका में सभी फ़ाइलों को जल्दी से क्रम में करने की आवश्यकता होगी।

पहले, 5 मेगाबाइट की तरह एक्स से बफर आकार बढ़ाएं।

#define BUF_SIZE 1024*1024*5

फिर मुख्य लूप को संशोधित करें जहां यह निर्देशिका में प्रत्येक फ़ाइल के बारे में जानकारी को प्रिंट करने के लिए इनकोड के साथ प्रविष्टियों को छोड़ देता है == 0. मैंने इसे जोड़कर किया था

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

मेरे मामले में मैं वास्तव में केवल निर्देशिका में फ़ाइल नामों के बारे में परवाह करता था इसलिए मैंने केवल फ़ाइल नाम को प्रिंट करने के लिए प्रिंटफ () विवरण को फिर से लिखा था।

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

इसे संकलित करें (इसे किसी बाहरी पुस्तकालयों की आवश्यकता नहीं है, इसलिए ऐसा करना सरल है)

gcc listdir.c -o listdir

अब बस चलाओ

./listdir [directory with insane number of files]

ध्यान दें कि लिनक्स एक रीड-फॉरवर्ड करता है, इसलिए readdir()वास्तव में धीमा नहीं है। इससे पहले कि मुझे विश्वास हो कि इस प्रदर्शन लाभ के लिए पोर्टेबिलिटी को फेंकने लायक है, मुझे ठोस आंकड़ा चाहिए।
fuz

-1

मैं एक निर्देशिका में फ़ाइलों की संख्या में परिवर्तन का ट्रैक रखने के लिए निम्न कमांड पसंद करता हूं।

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

कमांड उन फ़ाइलों की संख्या पर नज़र रखने के लिए एक खिड़की खुली रखेगा जो निर्देशिका में 0.1 सेकंड की ताज़ा दर के साथ हैं।


क्या आप सुनिश्चित हैं कि ls | wc -l0.01 में हजारों या लाखों फ़ाइलों के साथ एक फ़ोल्डर के लिए खत्म हो जाएगा? यहां तक ​​कि आपके lsअन्य समाधानों की तुलना में बेहद अक्षम है। और ओपी केवल गिनती प्राप्त करना चाहता है, वहां नहीं बैठकर आउटपुट को बदलते हुए देख रहा है
phuclv

कुंआ। कुंआ। मुझे एक सुंदर समाधान मिला जो मेरे लिए काम करता है। मैं वही साझा करना चाहूंगा, इसलिए किया। मुझे नहीं पता कि linux में 'ls' कमांड अत्यधिक अक्षम है। उसके बदले आप क्या उपयोग कर रहे हैं? और 0.01s रिफ्रेश रेट है। समय नहीं है। अगर आपने घड़ी का इस्तेमाल नहीं किया है तो कृपया मैन पेज देखें।
अनूप टॉफी

अच्छी तरह से मैंने watchउस टिप्पणी के बाद मैनुअल को पढ़ा और देखा कि 0.01s (0.1s नहीं) एक अवास्तविक संख्या है क्योंकि अधिकांश पीसी स्क्रीन की ताज़ा दर केवल 60Hz है, और यह किसी भी तरह से सवाल का जवाब नहीं देता है। ओपी ने "बड़ी संख्या में फ़ाइलों के लिए फास्ट लिनक्स फ़ाइल गणना" के बारे में पूछा। आपने पोस्ट करने से पहले कोई भी उपलब्ध उत्तर नहीं पढ़ा है
phuclv

मैंने उत्तर पढ़ लिए। लेकिन मैंने जो पोस्ट किया है वह एक निर्देशिका में फ़ाइल की बदलती संख्या का ट्रैक रखने का एक तरीका है। उदाहरण के लिए: फ़ाइल को एक स्थान से दूसरे स्थान पर कॉपी करते समय फ़ाइल की संख्या में परिवर्तन होता रहता है। विधि मैं पोस्टर के साथ एक का ट्रैक रख सकते हैं। मैं मानता हूं कि मैंने जो पद बनाया है, उसमें किसी भी पिछले पद को संशोधित या सुधार नहीं किया है।
अनूप टॉफी

-2

फ़ाइलों के हिगस्ट नं के साथ पहले 10 निर्देशकों।

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

3
यह निश्चित रूप से शक्तिशाली लोगों द्वारा लिखे गए उत्तर (समान कीड़े के साथ) के समान आश्चर्यजनक लगता है । यदि आप किसी अन्य व्यक्ति द्वारा लिखे गए कोड को बढ़ाने या संशोधित करने जा रहे हैं, तो उन्हें जमा करना उचित है। अपने उत्तरों में जिस कोड का आप उपयोग कर रहे हैं, उसे समझना और उसके कीड़े को ठीक करना और भी अधिक उचित है।
चार्ल्स डफी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.