Ls -R को "पुनरावर्ती" सूची क्यों कहा जाता है?


36

मैं समझता हूं कि ls -Rनिर्देशिकाओं की एक सूची प्रदर्शित करता है। लेकिन यह पुनरावर्ती क्यों है? प्रक्रिया में पुनरावृत्ति का उपयोग कैसे किया जाता है?


12
अंतर्ज्ञान यह है कि निर्देशिकाओं और उनकी उपनिर्देशिकाओं को आसानी से एक पेड़ का उपयोग करके मॉडलिंग की जा सकती है। पेड़ों को चलने के लिए एल्गोरिदम आमतौर पर पुनरावर्ती होते हैं।
केविन -

1
@ केविन मुझे नहीं लगता कि प्रत्येक प्रश्न का उत्तर देने के लिए पेड़ों की अवधारणा को लागू करने की कोई आवश्यकता है - जवाब बस इतना है कि जब lsएक निर्देशिका का सामना करता है तो वह उस निर्देशिका को पुन: सूचीबद्ध करता है।
user253751

जवाबों:


67

सबसे पहले, चलो एक मनमाना फ़ोल्डर संरचना परिभाषित करते हैं:

.
├── a1 [D]
│   ├── b1 [D]
│   │   ├── c1
│   │   ├── c2 [D]
│   │   │   ├── d1
│   │   │   ├── d2
│   │   │   └── d3
│   │   ├── c3
│   │   ├── c4
│   │   └── c5
│   └── b2 [D]
│       ├── c6
│       └── c7
├── a2 [D]
│   ├── b3 [D]
│   │   ├── c8
│   │   └── c9
│   └── b4 [D]
│       ├── c10 
│       └── c11
├── a3 [D]
│   ├── b5
│   ├── b6
│   └── b7
└── a4 [D]

जब हम ऐसा करते हैं ls, हमें केवल बेस फोल्डर का आउटपुट मिलता है:

a1 a2 a3 a4

हालाँकि, जब हम कॉल करते हैं ls -R, तो हमें कुछ अलग मिलता है:

.:
a1  a2  a3  a4

./a1:
b1  b2

./a1/b1:
c1  c2  c3  c4  c5

./a1/b1/c2:
d1  d2  d3

./a1/b2:
c6  c7

./a2:
b3  b4

./a2/b3:
c8  c9

./a2/b4:
c10  c11

./a3:
b5  b6  b7

./a4:

जैसा कि आप देख सकते हैं, यह lsआधार फ़ोल्डर पर चल रहा है , और फिर सभी बच्चे फ़ोल्डर। और सभी पोते फ़ोल्डर, विज्ञापन infinitum। प्रभावी रूप से, कमांड प्रत्येक फ़ोल्डर के माध्यम से पुनरावर्ती रूप से जा रहा है जब तक कि यह निर्देशिका ट्री के अंत को हिट नहीं करता है। उस बिंदु पर, यह पेड़ में एक शाखा वापस आता है और किसी भी उप-फ़ोल्डर के लिए एक ही काम करता है, यदि कोई हो।

या, छद्म कोड में:

recursivelyList(directory) {
    files[] = listDirectory(directory)              // Get all files in the directory
    print(directory.name + ":\n" + files.join(" ")) // Print the "ls" output
    for (file : files) {                            // Go through all the files in the directory
        if (file.isDirectory()) {                   // Check if the current file being looked at is a directory
            recursivelyList(directory)              // If so, recursively list that directory
        }
    }
}

और क्योंकि मैं कर सकता हूँ, एक संदर्भ जावा उसी का कार्यान्वयन


23

प्रभाव में, दो करीबी युग्मित प्रश्न जो आप पूछ रहे हैं।

  • फाइलसिस्टम पदानुक्रम में प्रत्येक प्रविष्टि के लिए चलने की प्रक्रिया एक अंतर्निहित पुनरावर्ती प्रक्रिया क्यों है? इसे अन्य उत्तरों से संबोधित किया जाता है, जैसे कि ज़न्ना और काज़ वोल्फ के
  • के कार्यान्वयन में पुनरावृत्ति की तकनीक का उपयोग कैसे किया जाता है ls? आपके वाक्यांश ("प्रक्रिया में पुनरावृत्ति का उपयोग कैसे किया जाता है") से, मुझे लगता है कि यह वही है जो आप जानना चाहते हैं। यह उत्तर उस प्रश्न को संबोधित करता है।

क्यों यह lsएक पुनरावर्ती तकनीक के साथ लागू करने के लिए समझ में आता है:

FOLDOC पुनरावृत्ति को परिभाषित करता है :

जब कोई फ़ंक्शन (या प्रक्रिया ) स्वयं कॉल करता है। ऐसे फ़ंक्शन को "पुनरावर्ती" कहा जाता है। यदि कॉल एक या एक से अधिक फ़ंक्शन के माध्यम से होता है, तो फ़ंक्शन के इस समूह को "पारस्परिक रूप से पुनरावर्ती" कहा जाता है।

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

विकल्प प्रसंस्करण के दौरान, lsयह निर्धारित करेगा कि क्या इसे पुनरावर्ती रूप से संचालित करने के लिए कहा गया है ( -Rध्वज के साथ लागू होने से )। यदि हां, तो जो फ़ंक्शन प्रदर्शित होने वाली प्रविष्टियों की सूची बनाता है, वह प्रत्येक निर्देशिका के लिए एक बार खुद को कॉल करेगा, सिवाय इसके .और ..। इस फ़ंक्शन के अलग-अलग पुनरावर्ती और गैर-आवेगी संस्करण हो सकते हैं, या फ़ंक्शन हर बार जांच कर सकता है कि क्या इसे पुनरावर्ती रूप से संचालित किया जाना है।

उबंटू की /bin/ls, निष्पादन योग्य जो आपके चलने पर चलती है ls, जीएनयू कोरुटिल्स द्वारा प्रदान की जाती है , और इसमें कई विशेषताएं हैं। परिणामस्वरूप, इसका कोड आपकी अपेक्षा से कुछ अधिक लंबा और अधिक जटिल है। लेकिन Ubuntu में बिजीबॉक्सls द्वारा प्रदान किया गया एक सरल संस्करण भी है । आप इसे टाइप करके चला सकते हैं busybox ls

कैसे busybox lsपुनरावर्तन का उपयोग करता है:

lsमें बिजीबॉक्स कार्यान्वित किया जाता है coreutils/ls.c। इसमें एक ऐसा scan_and_display_dirs_recur()फंक्शन होता है जो एक डायरेक्टरी ट्री को पुनरावर्ती रूप से प्रिंट करने के लिए लगाया जाता है:

static void scan_and_display_dirs_recur(struct dnode **dn, int first)
{
    unsigned nfiles;
    struct dnode **subdnp;

    for (; *dn; dn++) {
        if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
            if (!first)
                bb_putchar('\n');
            first = 0;
            printf("%s:\n", (*dn)->fullname);
        }
        subdnp = scan_one_dir((*dn)->fullname, &nfiles);
#if ENABLE_DESKTOP
        if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
            printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
#endif
        if (nfiles > 0) {
            /* list all files at this level */
            sort_and_display_files(subdnp, nfiles);

            if (ENABLE_FEATURE_LS_RECURSIVE
             && (G.all_fmt & DISP_RECURSIVE)
            ) {
                struct dnode **dnd;
                unsigned dndirs;
                /* recursive - list the sub-dirs */
                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
                dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
                if (dndirs > 0) {
                    dnsort(dnd, dndirs);
                    scan_and_display_dirs_recur(dnd, 0);
                    /* free the array of dnode pointers to the dirs */
                    free(dnd);
                }
            }
            /* free the dnodes and the fullname mem */
            dfree(subdnp);
        }
    }
}

वह रेखा जहाँ पुनरावर्ती फ़ंक्शन कॉल होती है:

                    scan_and_display_dirs_recur(dnd, 0);

पुनरावर्ती फ़ंक्शन कॉल को देखते हुए वे होते हैं:

यदि आप busybox lsडिबगर में चलाते हैं , तो आप इसे ऑपरेशन में देख सकते हैं । पहले -dbgsym.ddeb संकुल को सक्षम करके डीबग प्रतीकों को स्थापित करें और फिर पैकेज को स्थापित करें । साथ ही स्थापित करें (यह डीबगर है)।busybox-static-dbgsymgdb

sudo apt-get update
sudo apt-get install gdb busybox-static-dbgsym

मैं coreutils lsएक साधारण निर्देशिका पेड़ पर डिबगिंग का सुझाव देता हूं ।

यदि आपके पास एक काम नहीं है, तो एक बनाओ (यह WinEunuuchs2Unix के जवाबmkdir -p में कमांड के समान काम करता है :

mkdir -pv foo/{bar/foobar,baz/quux}

और इसे कुछ फाइलों के साथ आबाद करें:

(shopt -s globstar; for d in foo/**; do touch "$d/file$((i++))"; done)

आप busybox ls -R fooइस आउटपुट का निर्माण करके अपेक्षा के अनुरूप कार्यों का सत्यापन कर सकते हैं :

foo:
bar    baz    file0

foo/bar:
file1   foobar

foo/bar/foobar:
file2

foo/baz:
file3  quux

foo/baz/quux:
file4

busyboxडिबगर में खोलें :

gdb busybox

GDB अपने बारे में कुछ जानकारी छापेगा। तो इसे कुछ इस तरह कहना चाहिए:

Reading symbols from busybox...Reading symbols from /usr/lib/debug/.build-id/5c/e436575b628a8f54c2a346cc6e758d494c33fe.debug...done.
done.
(gdb)

(gdb)डीबगर में आपका संकेत है। इस प्रांप्ट पर करने के लिए सबसे पहले आपको GDB बताएगा कि scan_and_display_dirs_recur()फंक्शन की शुरुआत में एक ब्रेकप्वाइंट सेट करना है:

b scan_and_display_dirs_recur

जब आप इसे चलाते हैं, तो GDB को आपको कुछ इस तरह बताना चाहिए:

Breakpoint 1 at 0x5545b4: file coreutils/ls.c, line 1026.

अब GDB बताने को चलाने के लिए busyboxके साथ (आप चाहते हैं या जो कुछ भी निर्देशिका का नाम) अपने तर्कों के रूप में:ls -R foo

run ls -R foo

आप कुछ इस तरह देख सकते हैं:

Starting program: /bin/busybox ls -R foo

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6c60, first=1) at coreutils/ls.c:1026
1026    coreutils/ls.c: No such file or directory.

यदि आप देखते हैं No such file or directory, जैसा कि ऊपर, ठीक है। इस प्रदर्शन का उद्देश्य केवल यह देखना है कि scan_and_display_dirs_recur()फ़ंक्शन को कब बुलाया गया है, इसलिए GDB को वास्तविक स्रोत कोड की जांच करने की आवश्यकता नहीं है।

ध्यान दें कि किसी भी डायरेक्टरी की प्रविष्टियाँ छपने से पहले ही डिबगर ने ब्रेकपॉइंट को हिट कर दिया था। यह उस फ़ंक्शन के एंट्रेस पर टूट जाता है , लेकिन उस फ़ंक्शन के कोड को प्रिंटिंग के लिए किसी भी निर्देशिका के लिए चलना चाहिए।

जारी रखने के लिए GDB बताने के लिए, दौड़ें:

c

प्रत्येक बार scan_and_display_dirs_recur()कॉल किया जाता है, ब्रेकपॉइंट फिर से मारा जाएगा, इसलिए आपको कार्रवाई में पुनरावृत्ति देखने को मिलेगी। यह इस तरह दिखता है ( (gdb)शीघ्र और आपके आदेश सहित ):

(gdb) c
Continuing.
foo:
bar    baz    file0

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cb0, first=first@entry=0) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.

foo/bar:
file1   foobar

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cf0, first=first@entry=0) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.

foo/bar/foobar:
file2

foo/baz:
file3  quux

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cf0, first=first@entry=0) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.

foo/baz/quux:
file4
[Inferior 1 (process 2321) exited normally]

फ़ंक्शन के recurनाम पर है ... क्या फ़ॉर्गीबॉक्स केवल इसका उपयोग करता है जब -Rझंडा दिया जाता है? डिबगर में, यह पता लगाना आसान है:

(gdb) run ls foo
Starting program: /bin/busybox ls foo

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6c60, first=1) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.
bar    baz    file0
[Inferior 1 (process 2327) exited normally]

इसके बिना भी -R, यह विशेष कार्यान्वयन lsसमान फ़ंक्शन का उपयोग करके यह पता लगाने के लिए करता है कि फाइलसिस्टम प्रविष्टियां मौजूद हैं और उन्हें दिखाती हैं।

जब आप डिबगर को छोड़ना चाहते हैं, तो इसे बताएं:

q

scan_and_display_dirs_recur()यह कैसे पता चलेगा कि उसे स्वयं कॉल करना चाहिए:

विशेष रूप से जब -Rझंडा पास किया जाता है तो यह अलग तरह से कैसे काम करता है? जांच स्रोत कोड (कि अपने Ubuntu पर सही संस्करण नहीं हो सकता है) से पता चलता है कि वह अपने आंतरिक डेटा संरचना की जाँच करता है G.all_fmt, जहां यह भंडार क्या विकल्प इसके साथ लागू कर दिया गया है

            if (ENABLE_FEATURE_LS_RECURSIVE
             && (G.all_fmt & DISP_RECURSIVE)

(यदि बिजीबॉक्स को बिना समर्थन के संकलित किया गया है -R, तो यह फ़ाइलसिस्टम प्रविष्टियों को पुन: प्रदर्शित करने का प्रयास नहीं करेगा। यही वह ENABLE_FEATURE_LS_RECURSIVEहिस्सा है।)

केवल जब G.all_fmt & DISP_RECURSIVEयह सच होता है कोड जिसमें पुनरावर्ती फ़ंक्शन कॉल शामिल है रन हो जाता है।

                struct dnode **dnd;
                unsigned dndirs;
                /* recursive - list the sub-dirs */
                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
                dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
                if (dndirs > 0) {
                    dnsort(dnd, dndirs);
                    scan_and_display_dirs_recur(dnd, 0);
                    /* free the array of dnode pointers to the dirs */
                    free(dnd);
                }

अन्यथा, फ़ंक्शन सिर्फ एक बार चलता है (कमांड लाइन पर निर्दिष्ट प्रति निर्देशिका)।


एक बार फिर, एलियाह एक अति-व्यापक उत्तर के साथ आता है। एक अच्छी तरह से योग्य +1।
कज़ वोल्फ

2
ओह, तो यह भी पूंछ पुनरावृत्ति नहीं है। इसका मतलब यह होना चाहिए कि कुछ निर्देशिका सामग्री मौजूद है, लिस्टिंग जो स्टैक ओवरफ्लो के कारण व्यस्त बॉक्स को क्रैश कर देगी (हालांकि यह एक बहुत गहरी नेस्टिंग होगी)।
रुस्लान

2
यह अचरज भरा है। आपने मूल रूप से डिबगिंग में एक त्वरित पाठ के साथ ओपी प्रदान किया ताकि वे समझ सकें कि वास्तव में यह कैसे काम कर रहा है। उत्तम।
एंड्रिया लज्जाज़ारो

16

जब आप इसके बारे में सोचते हैं, "पुनरावर्ती" आदेशों और निर्देशिकाओं और उनकी फ़ाइलों और निर्देशिकाओं और उनकी फ़ाइलों और निर्देशिकाओं और उनकी फ़ाइलों और निर्देशिकाओं और उनकी फ़ाइलों पर कार्य करने के लिए समझ में आता है .........

.... जब तक निर्दिष्ट बिंदु से पूरे पेड़ को कमांड द्वारा संचालित नहीं किया गया है, इस मामले में किसी भी उपनिर्देशिका के किसी भी उपनिर्देशिका की सामग्री को सूचीबद्ध कर रहा है .......... जो कि नीचे मौजूद है तर्क (ओं) का आदेश


7

-R पुनरावृत्ति के लिए है, जिसे शिथिल रूप से "बार-बार" कहा जा सकता है।

इस कोड को उदाहरण के लिए लें:

───────────────────────────────────────────────────────────────────────────────
$ mkdir -p temp/a
───────────────────────────────────────────────────────────────────────────────
$ mkdir -p temp/b/1
───────────────────────────────────────────────────────────────────────────────
$ mkdir -p temp/c/1/2
───────────────────────────────────────────────────────────────────────────────
$ ls -R temp
temp:
a  b  c

temp/a:

temp/b:
1

temp/b/1:

temp/c:
1

temp/c/1:
2

temp/c/1/2:

-pनिर्देशिका बनाने में आप जन एक भी आदेश के साथ निर्देशिका बनाने के लिए अनुमति देता है। यदि एक या अधिक शीर्ष-मध्य निर्देशिका पहले से मौजूद हैं तो यह त्रुटि नहीं है और मध्य-निम्न निर्देशिका बनाई जाती है।

फिर ls -Rपुनरावर्ती प्रत्येक एकल निर्देशिका को अस्थायी से शुरू करती है और काम करती है और यह पेड़ की सभी शाखाओं तक जाती है।

अब ls -Rकमांड के पूरक को देखते हैं , अर्थात treeकमांड:

$ tree temp
temp
├── a
├── b
│   └── 1
└── c
    └── 1
        └── 2

6 directories, 0 files

जैसा कि आप देख सकते हैं के treeरूप में ही पूरा करता है ls -Rसिवाय अधिक संक्षिप्त और हिम्मत है मैं कहता हूँ "सुंदर"।

अब देखते हैं कि हम उन निर्देशिकाओं को कैसे हटा सकते हैं जिन्हें हमने केवल एक साधारण कमांड में बनाया था:

$ rm -r temp

यह पुन: हटाता है tempऔर इसके नीचे सभी उप-निर्देशिकाएँ। यानी temp/a, temp/b/1और temp/c/1/2प्लस बीच निर्देशिकाओं के बीच।


अगर "ls -R" बार-बार कुछ करना है तो आपको एक ही आउटपुट कई बार मिलेगा;) treeहालांकि के लिए +1 । यह एक महान उपकरण है।
पॉड

हाँ आम आदमी की आवाज खराब। मैं मुख्यधारा में एक शब्द खोजने की कोशिश कर रहा था जिससे गैर प्रोग्रामर प्रकारों को समझने में आसानी हो। मैं बेहतर शब्द सोचने या बाद में हटाने की कोशिश करूँगा।
विनयुनुच्स 2 यूनिक्स

5

यहां एक सरल व्याख्या है, यह समझ में आता है क्योंकि जब उपनिर्देशिका की सामग्री प्रदर्शित करने की बात आती है, तो वही फ़ंक्शन पहले से ही जानता है कि एक निर्देशिका के साथ क्या करना है। इसलिए यह सिर्फ उस परिणाम को प्राप्त करने के लिए प्रत्येक उपनिर्देशिका पर खुद को कॉल करने की आवश्यकता है!

छद्मकोड में यह कुछ इस तरह दिखता है:

recursive_ls(dir)
    print(files and directories)
    foreach (directoriy in dir)
        recursive_ls(directory)
    end foreach
end recursive_ls
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.