सवाल लिनक्स पर सी में निर्देशिकाओं को दोबारा कैसे सूचीबद्ध करें?


मुझे सी प्रोग्रामिंग में सभी निर्देशिकाओं और फ़ाइलों को दोबारा सूचीबद्ध करने की आवश्यकता है। मैंने एफटीडब्ल्यू में देखा है लेकिन यह 2 ऑपरेटिंग सिस्टमों के साथ शामिल नहीं है जो मैं उपयोग कर रहा हूं (फेडोरा और मिनिक्स)। मुझे पिछले कुछ घंटों में पढ़ने वाली सभी अलग-अलग चीजों से बड़ा सिरदर्द मिलना शुरू हो रहा है।

अगर किसी को कोड स्निपेट के बारे में पता है तो मैं देख सकता हूं कि यह आश्चर्यजनक होगा, या अगर कोई मुझे इस पर अच्छी दिशा दे सकता है तो मैं बहुत आभारी रहूंगा।


44
2017-12-08 19:57


मूल


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


जवाब:


यहां एक पुनरावर्ती संस्करण है:

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>

void listdir(const char *name, int indent)
{
    DIR *dir;
    struct dirent *entry;

    if (!(dir = opendir(name)))
        return;

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_DIR) {
            char path[1024];
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
            snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
            printf("%*s[%s]\n", indent, "", entry->d_name);
            listdir(path, indent + 2);
        } else {
            printf("%*s- %s\n", indent, "", entry->d_name);
        }
    }
    closedir(dir);
}

int main(void) {
    listdir(".", 0);
    return 0;
}

49
2017-12-08 22:34



<Dirent.h> में परिभाषित किया जाना चाहिए। आप इसे किस प्लेटफॉर्म पर संकलित कर रहे हैं? - Lloyd Macrohon
फेडोरा के तहत जीसीसी। - Charlie
यह वास्तव में आईडीई के साथ बनाया गया था जिसे मैं पसंद नहीं कर रहा था, यह टर्मिनल में जीसीसी के माध्यम से ठीक चला गया - Charlie
ओह बीटीडब्ल्यू, इस कोड को बदलें, जबकि यह है ((प्रविष्टि = readdir (dir)) और अगर (! (Entry = readdir (dir)) को हटा दें, या कम से कम अगर readdir विफल हो जाता है, तो सुनिश्चित करें कि आप लौटने से पहले बंदर को कॉल करें। - Lloyd Macrohon
चक्रीय लिंक की उपस्थिति में यह कैसे व्यवहार करता है? - Kerrek SB


हर कोई पहिया को फिर से घुमाने पर जोर क्यों देता है?

POSIX.1-2008 मानकीकृत nftw() फ़ंक्शन, एकल यूनिक्स विशिष्टता v4 (SuSv4) में भी परिभाषित किया गया है, और लिनक्स में उपलब्ध है (glibc, man 3 nftw), ओएस एक्स, और सबसे वर्तमान बीएसडी संस्करण। यह बिल्कुल नया नहीं है।

भोली opendir()/readdir()/closedir() -आधारित कार्यान्वयन लगभग उन मामलों को संभाल नहीं लेते हैं जहां पेड़ ट्रैवर्सल के दौरान निर्देशिका या फ़ाइलों को स्थानांतरित, नामित या हटा दिया जाता है, जबकि nftw() उन्हें कृपा से संभालना चाहिए।

उदाहरण के तौर पर, निम्न सी प्रोग्राम पर विचार करें जो वर्तमान कार्य निर्देशिका से शुरू होने वाले निर्देशिका पेड़ को सूचीबद्ध करता है, या कमांड लाइन पर नामित प्रत्येक निर्देशिका में, या केवल कमांड लाइन पर नाम की गई फ़ाइलों को सूचीबद्ध करता है:

/* We want POSIX.1-2008 + XSI, i.e. SuSv4, features */
#define _XOPEN_SOURCE 700

/* Added on 2017-06-25:
   If the C library can support 64-bit file sizes
   and offsets, using the standard names,
   these defines tell the C library to do so. */
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64 

#include <stdlib.h>
#include <unistd.h>
#include <ftw.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

/* POSIX.1 says each process has at least 20 file descriptors.
 * Three of those belong to the standard streams.
 * Here, we use a conservative estimate of 15 available;
 * assuming we use at most two for other uses in this program,
 * we should never run into any problems.
 * Most trees are shallower than that, so it is efficient.
 * Deeper trees are traversed fine, just a bit slower.
 * (Linux allows typically hundreds to thousands of open files,
 *  so you'll probably never see any issues even if you used
 *  a much higher value, say a couple of hundred, but
 *  15 is a safe, reasonable value.)
*/
#ifndef USE_FDS
#define USE_FDS 15
#endif

int print_entry(const char *filepath, const struct stat *info,
                const int typeflag, struct FTW *pathinfo)
{
    /* const char *const filename = filepath + pathinfo->base; */
    const double bytes = (double)info->st_size; /* Not exact if large! */
    struct tm mtime;

    localtime_r(&(info->st_mtime), &mtime);

    printf("%04d-%02d-%02d %02d:%02d:%02d",
           mtime.tm_year+1900, mtime.tm_mon+1, mtime.tm_mday,
           mtime.tm_hour, mtime.tm_min, mtime.tm_sec);

    if (bytes >= 1099511627776.0)
        printf(" %9.3f TiB", bytes / 1099511627776.0);
    else
    if (bytes >= 1073741824.0)
        printf(" %9.3f GiB", bytes / 1073741824.0);
    else
    if (bytes >= 1048576.0)
        printf(" %9.3f MiB", bytes / 1048576.0);
    else
    if (bytes >= 1024.0)
        printf(" %9.3f KiB", bytes / 1024.0);
    else
        printf(" %9.0f B  ", bytes);

    if (typeflag == FTW_SL) {
        char   *target;
        size_t  maxlen = 1023;
        ssize_t len;

        while (1) {

            target = malloc(maxlen + 1);
            if (target == NULL)
                return ENOMEM;

            len = readlink(filepath, target, maxlen);
            if (len == (ssize_t)-1) {
                const int saved_errno = errno;
                free(target);
                return saved_errno;
            }
            if (len >= (ssize_t)maxlen) {
                free(target);
                maxlen += 1024;
                continue;
            }

            target[len] = '\0';
            break;
        }

        printf(" %s -> %s\n", filepath, target);
        free(target);

    } else
    if (typeflag == FTW_SLN)
        printf(" %s (dangling symlink)\n", filepath);
    else
    if (typeflag == FTW_F)
        printf(" %s\n", filepath);
    else
    if (typeflag == FTW_D || typeflag == FTW_DP)
        printf(" %s/\n", filepath);
    else
    if (typeflag == FTW_DNR)
        printf(" %s/ (unreadable)\n", filepath);
    else
        printf(" %s (unknown)\n", filepath);

    return 0;
}


int print_directory_tree(const char *const dirpath)
{
    int result;

    /* Invalid directory path? */
    if (dirpath == NULL || *dirpath == '\0')
        return errno = EINVAL;

    result = nftw(dirpath, print_entry, USE_FDS, FTW_PHYS);
    if (result >= 0)
        errno = result;

    return errno;
}

int main(int argc, char *argv[])
{
    int arg;

    if (argc < 2) {

        if (print_directory_tree(".")) {
            fprintf(stderr, "%s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

    } else {

        for (arg = 1; arg < argc; arg++) {
            if (print_directory_tree(argv[arg])) {
                fprintf(stderr, "%s.\n", strerror(errno));
                return EXIT_FAILURE;
            }
        }

    }

    return EXIT_SUCCESS;
}

उपर्युक्त कोड में से अधिकांश है print_entry()। इसका कार्य प्रत्येक निर्देशिका प्रविष्टि को मुद्रित करना है। में print_directory_tree(), हम कहते हैं nftw() प्रत्येक निर्देशिका प्रविष्टि के लिए इसे कॉल करने के लिए इसे कॉल करें।

उपरोक्त एकमात्र हाथ-लहर विवरण यह निर्णय है कि कितने फ़ाइल डिस्क्रिप्टर को देना चाहिए nftw() उपयोग। यदि आपका प्रोग्राम फ़ाइल पेड़ की सैर के दौरान अधिकतम दो अतिरिक्त फ़ाइल डिस्क्रिप्टर (मानक धाराओं के अतिरिक्त) पर उपयोग करता है, तो 15 सुरक्षित होने के लिए जाना जाता है (सभी प्रणालियों पर nftw() और ज्यादातर POSIX- अनुपालन होने के नाते)।

लिनक्स में, आप इसका उपयोग कर सकते हैं sysconf(_SC_OPEN_MAX) खुली फ़ाइलों की अधिकतम संख्या को खोजने के लिए, और उस संख्या को घटाएं जिसका आप उपयोग करते हैं nftw() कॉल करें, लेकिन मुझे परेशान नहीं होगा (जब तक मुझे पता नहीं था कि उपयोगिता का उपयोग पैथोलॉजिकल गहरी निर्देशिका संरचनाओं के साथ किया जाएगा)। पंद्रह वर्णनकर्ता करता है नहीं पेड़ की गहराई को सीमित करें; nftw() बस धीमा हो जाता है (और उस निर्देशिका से परिवर्तनों का पता नहीं लगा सकता है यदि उस निर्देशिका से 13 निर्देशिकाओं से गहराई से निर्देशिका चलती है, हालांकि व्यापार और सी पुस्तकालय कार्यान्वयन के बीच परिवर्तनों का पता लगाने की सामान्य क्षमता भिन्न होती है)। बस एक संकलन-समय निरंतर उपयोग करके कोड पोर्टेबल रखता है - इसे केवल लिनक्स पर ही नहीं, बल्कि मैक ओएस एक्स और सभी मौजूदा बीएसडी संस्करणों पर काम करना चाहिए, और अधिकांश अन्य पुराने यूनिक्स वेरिएंट भी नहीं।

एक टिप्पणी में, रुस्लान ने उल्लेख किया कि उन्हें स्विच करना था nftw64() क्योंकि उनके पास फाइल सिस्टम प्रविष्टियां थीं जिनके लिए 64-बिट आकार / ऑफसेट और "सामान्य" संस्करण की आवश्यकता थी nftw() के साथ असफल errno == EOVERFLOW। सही समाधान GLIBC- विशिष्ट 64-बिट फ़ंक्शंस पर स्विच नहीं करना है, लेकिन परिभाषित करना है _LARGEFILE64_SOURCE तथा _FILE_OFFSET_BITS 64। मानक कार्यों का उपयोग करते समय ये संभवतः 64-बिट फ़ाइल आकार और ऑफसेट पर स्विच करने के लिए सी लाइब्रेरी को बताते हैं (nftw(), fstat(), et cetera) और प्रकार के नाम (off_t आदि।)।


61
2018-04-01 23:37



इस विशेष पहिया को पुनर्निर्मित करने का प्राथमिक कारण यह है कि इसे उपयोग में एक अभ्यास के रूप में सेट किया गया है opendir(), readdir(), closedir(), और वस्तु लोगों को पूर्ण पथ नाम बनाम निर्देशिका प्रविष्टियों के बारे में सावधानी से सोचने के लिए सिखाना है। उत्पादन कोड में, nftw() जाने का रास्ता है - मुझे गलत मत समझो। लेकिन कच्चे कार्यों का उपयोग करके अभ्यास के लिए एक शैक्षिक कारण भी है। - Jonathan Leffler
@moodboom: संपूर्ण फाइल सिस्टम अनिवार्य रूप से एक गैर थ्रेड-सुरक्षित वैश्विक, सकल है। सिर्फ इसलिए कि एक कोडिंग गुरु कहते हैं कि ग्लोबल सकल हैं इसका मतलब यह नहीं है कि उनके पास वैध उपयोग नहीं हैं। निर्देशिका पेड़ ट्रैवर्सल सही ढंग से सरल नहीं है, और मैंने देखा है कि सबसे अधिक दावा किए गए सही उदाहरण मेरे आंतरिक फूल के रूप में नाजुक हैं। बहुत नाजुक मै इस्तेमाल करूंगा fts यदि इसे पॉज़िक्स में परिभाषित किया गया था, लेकिन हां, यह केवल 4.4BSD है, और समर्थन बिखरा हुआ है। जीएनयू सी लाइब्रेरी में भी, समर्थन (उदाहरण के लिए बड़ी फाइलों के लिए) 2.23 तक था। जुड़ी चर्चा सिर्फ मूर्ख है। - Nominal Animal
@moodboom: ठीक है। ध्यान दें कि मेरे उत्तर का मुख्य बिंदु यह है कि उपयोग करने का सामान्य उदाहरण opendir()/readdir()/closedir() असुरक्षित और दौड़ के लिए प्रवण है, और यदि आप चल रहे हैं, तो आप वास्तव में अजीब सामान कर सकते हैं, जबकि चलने वाली निर्देशिकाएं चलती हैं। यह है खराब कोड। नाजुक। nftw() और अन्य। उन सभी को सही ढंग से संभालने के लिए माना जाता है। हम POSIX.1-2008 का उपयोग कर एक सुरक्षित निर्देशिका पेड़ वॉकर लिख सकते हैं openat()/fdopendir()/readdir()/closedir(), लेकिन पेड़ की गहराई जो हम कर सकते हैं वह फाइल डिस्क्रिप्टरों की संख्या से सीमित होगी जो हम उपयोग कर सकते हैं। nftw() सुरक्षित रूप से उस से भी बचना चाहिए। - Nominal Animal
@gsamaras: :) मैं आपको cboard से जानता हूँ; हमने ईमेल किया है और सबकुछ, पिंजरे के मैचों के बारे में बात की है और इसी तरह। यदि आपने मेरा ई-मेल पता खो दिया है (अब मैं cboard का उपयोग नहीं करता - मेरे उत्तर स्पष्ट रूप से अवांछित थे, बहुत लंबे और वास्तव में पढ़ने के लिए बहुत विस्तृत थे), आप हमेशा मुझे ढूंढ सकते हैं जो मुझे पहुंचता है मेरा आमुख पृष्ठ। - Nominal Animal
मुझे विकल्पों के बारे में भी पता नहीं था *dir इस पोस्ट को देखने से पहले काम करता है। बहुत - बहुत धन्यवाद। - Daniel Kamil Kozar


int is_directory_we_want_to_list(const char *parent, char *name) {
  struct stat st_buf;
  if (!strcmp(".", name) || !strcmp("..", name))
    return 0;
  char *path = alloca(strlen(name) + strlen(parent) + 2);
  sprintf(path, "%s/%s", parent, name);
  stat(path, &st_buf);
  return S_ISDIR(st_buf.st_mode);
}

int list(const char *name) {
  DIR *dir = opendir(name);
  struct dirent *ent;
  while (ent = readdir(dir)) {
    char *entry_name = ent->d_name;
    printf("%s\n", entry_name);
    if (is_directory_we_want_to_list(name, entry_name)) {
      // You can consider using alloca instead.
      char *next = malloc(strlen(name) + strlen(entry_name) + 2);
      sprintf(next, "%s/%s", name, entry_name);
      list(next);
      free(next);
    }
  }
  closedir(dir);
}

इस संदर्भ में स्किम किए जाने वाले हेडर फाइलें: stat.h, dirent.h। ध्यान रखें कि उपरोक्त कोड किसी भी त्रुटि के लिए जांच नहीं कर रहा है जो हो सकता है।

एक पूरी तरह से अलग दृष्टिकोण की पेशकश की है ftw ftw.h में परिभाषित


7
2017-12-08 20:05



आप रिकर्स नहीं कर रहे हैं। यह कम से कम ऐसा नहीं दिखता है। क्या आप कॉल करना चाहते थे list(entry_name) आपकी टिप्पणी के नीचे? - Jon
@ जोन, यह सच है, मैंने ओपी को शुरू करने में मदद करने के लिए सिर्फ एक कंकाल लिखा था। यदि यह पर्याप्त नहीं है तो मैं विस्तृत कर सकता हूं। - Jan
तो यह सभी उप निर्देशिकाओं और उप निर्देशिकाओं के अंदर की सभी फाइलों के साथ निर्देशिका में सभी फ़ाइलों को सूचीबद्ध करेगा? - Charlie
यह एक स्निपेट की शुरुआत है जो करता है। उन्होंने टिप्पणी में रखा कि और क्या जोड़ा जाना चाहिए। पोस्ट के रूप में फ़ाइल बनाम डीआईआर के लिए कोई जांच नहीं है, या यह सुनिश्चित करने के लिए कि निर्देशिका नहीं है . या .., और कोई रिकर्सिव कॉल नहीं है, इसलिए मुझे लगता है कि यह केवल दी गई निर्देशिका में फ़ाइलों को प्रिंट करेगा। - prelic
मैंने जवाब अपडेट कर लिया है। अब यह दी गई निर्देशिका की सामग्री सूचीबद्ध करता है और जिसके लिए उपनिर्देशिका में रिकर्स किया जाता है is_directory_we_want_to_list एक गैर-शून्य मान देता है। HTH। - Jan


जैसा कि मैंने अपनी टिप्पणी में उल्लेख किया है, मुझे विश्वास है कि इस कार्य में दो अंतर्निहित त्रुटियों के लिए एक पुनरावर्ती दृष्टिकोण है।

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

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

हालांकि, एक ही फाइल डिस्क्रिप्टर और लिंक्ड सूचियों के साथ रिकर्सन को प्रतिस्थापित करके इन मुद्दों से बचना बहुत आसान है।

मुझे लगता है कि यह एक स्कूल परियोजना नहीं है और यह किरण वैकल्पिक है।

यहां एक उदाहरण आवेदन है।

उपयोग a.out ./ फ़ोल्डर पेड़ देखने के लिए।

मैक्रोज़ और सामान के लिए मैं क्षमा चाहता हूं ... मैं आम तौर पर इनलाइन फ़ंक्शंस का उपयोग करता हूं, लेकिन मैंने सोचा कि कोड का पालन करना आसान होगा यदि यह सब एक ही फ़ंक्शन में था।

#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char const *argv[]) {
  /* print use instruction unless a folder name was given */
  if (argc < 2)
    fprintf(stderr,
            "\nuse:\n"
            "    %s <directory>\n"
            "for example:\n"
            "    %s ./\n\n",
            argv[0], argv[0]),
        exit(0);

  /*************** a small linked list macro implementation ***************/

  typedef struct list_s {
    struct list_s *next;
    struct list_s *prev;
  } list_s;

#define LIST_INIT(name)                                                        \
  { .next = &name, .prev = &name }

#define LIST_PUSH(dest, node)                                                  \
  do {                                                                         \
    (node)->next = (dest)->next;                                               \
    (node)->prev = (dest);                                                     \
    (node)->next->prev = (node);                                               \
    (dest)->next = (node);                                                     \
  } while (0);

#define LIST_POP(list, var)                                                    \
  if ((list)->next == (list)) {                                                \
    var = NULL;                                                                \
  } else {                                                                     \
    var = (list)->next;                                                        \
    (list)->next = var->next;                                                  \
    var->next->prev = var->prev;                                               \
  }

  /*************** a record (file / folder) item type ***************/

  typedef struct record_s {
    /* this is a flat processing queue. */
    list_s queue;
    /* this will list all queued and processed folders (cyclic protection) */
    list_s folders;
    /* this will list all the completed items (siblings and such) */
    list_s list;
    /* unique ID */
    ino_t ino;
    /* name length */
    size_t len;
    /* name string */
    char name[];
  } record_s;

/* take a list_s pointer and convert it to the record_s pointer */
#define NODE2RECORD(node, list_name)                                           \
  ((record_s *)(((uintptr_t)(node)) -                                          \
                ((uintptr_t) & ((record_s *)0)->list_name)))

/* initializes a new record */
#define RECORD_INIT(name)                                                      \
  (record_s){.queue = LIST_INIT((name).queue),                                 \
             .folders = LIST_INIT((name).folders),                             \
             .list = LIST_INIT((name).list)}

  /*************** the actual code ***************/

  record_s records = RECORD_INIT(records);
  record_s *pos, *item;
  list_s *tmp;
  DIR *dir;
  struct dirent *entry;

  /* initialize the root folder record and add it to the queue */
  pos = malloc(sizeof(*pos) + strlen(argv[1]) + 2);
  *pos = RECORD_INIT(*pos);
  pos->len = strlen(argv[1]);
  memcpy(pos->name, argv[1], pos->len);
  if (pos->name[pos->len - 1] != '/')
    pos->name[pos->len++] = '/';
  pos->name[pos->len] = 0;
  /* push to queue, but also push to list (first item processed) */
  LIST_PUSH(&records.queue, &pos->queue);
  LIST_PUSH(&records.list, &pos->list);

  /* as long as the queue has items to be processed, do so */
  while (records.queue.next != &records.queue) {
    /* pop queued item */
    LIST_POP(&records.queue, tmp);
    /* collect record to process */
    pos = NODE2RECORD(tmp, queue);
    /* add record to the processed folder list */
    LIST_PUSH(&records.folders, &pos->folders);

    /* process the folder and add all folder data to current list */
    dir = opendir(pos->name);
    if (!dir)
      continue;

    while ((entry = readdir(dir)) != NULL) {

      /* create new item, copying it's path data and unique ID */
      item = malloc(sizeof(*item) + pos->len + entry->d_namlen + 2);
      *item = RECORD_INIT(*item);
      item->len = pos->len + entry->d_namlen;
      memcpy(item->name, pos->name, pos->len);
      memcpy(item->name + pos->len, entry->d_name, entry->d_namlen);
      item->name[item->len] = 0;
      item->ino = entry->d_ino;
      /* add item to the list, right after the `pos` item */
      LIST_PUSH(&pos->list, &item->list);

      /* unless it's a folder, we're done. */
      if (entry->d_type != DT_DIR)
        continue;

      /* test for '.' and '..' */
      if (entry->d_name[0] == '.' &&
          (entry->d_name[1] == 0 ||
           (entry->d_name[1] == '.' && entry->d_name[2] == 0)))
        continue;

      /* add folder marker */
      item->name[item->len++] = '/';
      item->name[item->len] = 0;

      /* test for cyclic processing */
      list_s *t = records.folders.next;
      while (t != &records.folders) {
        if (NODE2RECORD(t, folders)->ino == item->ino) {
          /* we already processed this folder! */
          break; /* this breaks from the small loop... */
        }
        t = t->next;
      }
      if (t != &records.folders)
        continue; /* if we broke from the small loop, entry is done */

      /* item is a new folder, add to queue */
      LIST_PUSH(&records.queue, &item->queue);
    }
    closedir(dir);
  }

  /*************** Printing the results and cleaning up ***************/
  while (records.list.next != &records.list) {
    /* pop list item */
    LIST_POP(&records.list, tmp);
    /* collect record to process */
    pos = NODE2RECORD(tmp, list);
    /* prepare for next iteration */
    LIST_POP(&records.list, tmp);
    fwrite(pos->name, pos->len, 1, stderr);
    fwrite("\n", 1, 1, stderr);
    free(pos);
  }
  return 0;
}

संपादित करें

@Stargateur ने टिप्पणियों में उल्लेख किया है कि रिकर्सिव कोड खुली फ़ाइल सीमा तक पहुंचने से पहले ढेर को ओवरफ़्लो कर देगा।

हालांकि मुझे नहीं लगता कि स्टैक-ओवरफ्लो कैसे बेहतर होता है, यह मूल्यांकन तब तक सही होता है जब तक कि प्रक्रिया लागू होने पर फ़ाइल सीमा के करीब न हो।

टिप्पणियों में @Stargateur द्वारा उल्लिखित एक और बिंदु यह था कि रिकर्सिव कोड की गहराई अधिकतम उप-निर्देशिकाओं (644 ext4 फाइल सिस्टम पर 64000) तक सीमित है और हार्ड लिंक बेहद असंभव हैं (क्योंकि फ़ोल्डर के लिए हार्ड लिंक नहीं हैं लिनक्स / यूनिक्स पर अनुमति दी गई)।

यह अच्छी खबर है यदि कोड लिनक्स पर चल रहा है (जो यह है, प्रश्न के अनुसार), इसलिए यह समस्या वास्तविक चिंता नहीं है (जब तक मैकोज़ या शायद, विंडोज़ पर कोड नहीं चलाया जाता है ... हालांकि 64 के उपफोल्डर्स रिकर्सन में स्टैक चौड़ा खुला हो सकता है।

ऐसा कहकर, किसी भी पुनरावर्ती विकल्प में अभी भी फायदे नहीं हैं, जैसे प्रक्रियाओं को संसाधित करने के साथ-साथ परिणाम को कैश करने में सक्षम होने की आसानी से सीमा जोड़ने में सक्षम होना।

अनुलेख

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

#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char const *argv[]) {
  /* print use instruction unless a folder name was given */
  if (argc < 2)
    fprintf(stderr,
            "\nuse:\n"
            "    %s <directory>\n"
            "for example:\n"
            "    %s ./\n\n",
            argv[0], argv[0]),
        exit(0);

  /*************** a small linked list macro implementation ***************/

  typedef struct list_s {
    struct list_s *next;
    struct list_s *prev;
  } list_s;

#define LIST_INIT(name)                                                        \
  { .next = &name, .prev = &name }

#define LIST_PUSH(dest, node)                                                  \
  do {                                                                         \
    (node)->next = (dest)->next;                                               \
    (node)->prev = (dest);                                                     \
    (node)->next->prev = (node);                                               \
    (dest)->next = (node);                                                     \
  } while (0);

#define LIST_POP(list, var)                                                    \
  if ((list)->next == (list)) {                                                \
    var = NULL;                                                                \
  } else {                                                                     \
    var = (list)->next;                                                        \
    (list)->next = var->next;                                                  \
    var->next->prev = var->prev;                                               \
  }

  /*************** a record (file / folder) item type ***************/

  typedef struct record_s {
    /* this is a flat processing queue. */
    list_s queue;
    /* this will list all the completed items (siblings and such) */
    list_s list;
    /* unique ID */
    ino_t ino;
    /* name length */
    size_t len;
    /* name string */
    char name[];
  } record_s;

/* take a list_s pointer and convert it to the record_s pointer */
#define NODE2RECORD(node, list_name)                                           \
  ((record_s *)(((uintptr_t)(node)) -                                          \
                ((uintptr_t) & ((record_s *)0)->list_name)))

/* initializes a new record */
#define RECORD_INIT(name)                                                      \
  (record_s){.queue = LIST_INIT((name).queue), .list = LIST_INIT((name).list)}

  /*************** the actual code ***************/

  record_s records = RECORD_INIT(records);
  record_s *pos, *item;
  list_s *tmp;
  DIR *dir;
  struct dirent *entry;

  /* initialize the root folder record and add it to the queue */
  pos = malloc(sizeof(*pos) + strlen(argv[1]) + 2);
  *pos = RECORD_INIT(*pos);
  pos->len = strlen(argv[1]);
  memcpy(pos->name, argv[1], pos->len);
  if (pos->name[pos->len - 1] != '/')
    pos->name[pos->len++] = '/';
  pos->name[pos->len] = 0;
  /* push to queue, but also push to list (first item processed) */
  LIST_PUSH(&records.queue, &pos->queue);
  LIST_PUSH(&records.list, &pos->list);

  /* as long as the queue has items to be processed, do so */
  while (records.queue.next != &records.queue) {
    /* pop queued item */
    LIST_POP(&records.queue, tmp);
    /* collect record to process */
    pos = NODE2RECORD(tmp, queue);

    /* process the folder and add all folder data to current list */
    dir = opendir(pos->name);
    if (!dir)
      continue;

    while ((entry = readdir(dir)) != NULL) {

      /* create new item, copying it's path data and unique ID */
      item = malloc(sizeof(*item) + pos->len + entry->d_namlen + 2);
      *item = RECORD_INIT(*item);
      item->len = pos->len + entry->d_namlen;
      memcpy(item->name, pos->name, pos->len);
      memcpy(item->name + pos->len, entry->d_name, entry->d_namlen);
      item->name[item->len] = 0;
      item->ino = entry->d_ino;
      /* add item to the list, right after the `pos` item */
      LIST_PUSH(&pos->list, &item->list);

      /* unless it's a folder, we're done. */
      if (entry->d_type != DT_DIR)
        continue;

      /* test for '.' and '..' */
      if (entry->d_name[0] == '.' &&
          (entry->d_name[1] == 0 ||
           (entry->d_name[1] == '.' && entry->d_name[2] == 0)))
        continue;

      /* add folder marker */
      item->name[item->len++] = '/';
      item->name[item->len] = 0;

      /* item is a new folder, add to queue */
      LIST_PUSH(&records.queue, &item->queue);
    }
    closedir(dir);
  }

  /*************** Printing the results and cleaning up ***************/
  while (records.list.next != &records.list) {
    /* pop list item */
    LIST_POP(&records.list, tmp);
    /* collect record to process */
    pos = NODE2RECORD(tmp, list);
    /* prepare for next iteration */
    LIST_POP(&records.list, tmp);
    fwrite(pos->name, pos->len, 1, stderr);
    fwrite("\n", 1, 1, stderr);
    free(pos);
  }
  return 0;
}

4
2018-06-27 07:25



@Stargateur, यदि सर्वर पर रिकर्सिव कोड चल रहा है, तो आप शायद सही हैं - हालांकि, उस स्थिति में, स्टैक को बरकरार रखने और फ़ाइल डिस्क्रिप्टर के उपयोग को कम करने की संभावना प्राथमिकता है। साथ ही, यदि कोई चक्रीय फ़ोल्डर पदानुक्रम मौजूद है, तो उप-निर्देशिका सीमा से आपको बचाया नहीं जाएगा ... हालांकि, लिंक्ड सूची दृष्टिकोण एक स्टैक ओवरफ़्लो को रोकता है और यह कभी भी लटका या क्रैश नहीं होगा (यहां तक ​​कि एक बड़े पेड़ को संसाधित करते समय भी)। संसाधित वस्तुओं की संख्या पर एक सीमा जोड़ने के लिए भी काफी आसान है। मुझे इसकी सादगी के लिए रिकर्सिव कोड पसंद है, लेकिन यह अक्सर उपयोग करने के लिए और अधिक खतरनाक है और यह ढेर को धमकाता है। - Myst
@Stargateur हार्ड लिंक के बारे में क्या ...? क्या वे जोखिम नहीं हैं, या ओएस हमेशा चक्रीय पदानुक्रम बनाने से कड़ी लिंक को रोकता है? - Myst
मैकोज़ आधिकारिक तौर पर उन्हें अनुमति नहीं देता है, लेकिन उनका उपयोग टाइममैचिन के अंदर किया जाता है और इसे बनाया जा सकता है (हालांकि आम आदमी द्वारा नहीं) ... इसलिए मुझे एक प्रणाली पता है जहां ये हार्ड लिंक पॉप हो सकते हैं (या तो क्योंकि वे बनाए गए थे या कोड टाइममैचिन बैकअप वाले फ़ोल्डर में चलता है) ... हालांकि, आप जोखिम के संभावित होने के बारे में सही हो सकते हैं। - Myst
@Stargateur - मैंने आपकी टिप्पणियों को दर्शाने के लिए उत्तर अपडेट किया। - Myst


यहां एक सरलीकृत संस्करण है जो रिकर्सन का उपयोग करता है लेकिन बहुत कम स्टैक स्पेस का उपयोग करता है:

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>

void listdir(char *path, size_t size) {
    DIR *dir;
    struct dirent *entry;
    size_t len = strlen(path);

    if (!(dir = opendir(path))) {
        fprintf(stderr, "path not found: %s: %s\n",
                path, strerror(errno));
        return;
    }

    puts(path);
    while ((entry = readdir(dir)) != NULL) {
        char *name = entry->d_name;
        if (entry->d_type == DT_DIR) {
            if (!strcmp(name, ".") || !strcmp(name, ".."))
                continue;
            if (len + strlen(name) + 2 > size) {
                fprintf(stderr, "path too long: %s/%s\n", path, name);
            } else {
                path[len] = '/';
                strcpy(path + len + 1, name);
                listdir(path, size);
                path[len] = '\0';
            }
        } else {
            printf("%s/%s\n", path, name);
        }
    }
    closedir(dir);
}

int main(void) {
    char path[1024] = ".";
    listdir(path, sizeof path);
    return 0;
}

मेरे सिस्टम पर, इसका आउटपुट बिल्कुल समान है find .


2
2018-06-24 16:12



चार्ली के chqrlie जवाब, यह उलझन में है: पी। - Stargateur
@Stargateur: हाँ, मैंने यह भी देखा, लेकिन कोई कनेक्शन नहीं है। - chqrlie