मैं समझता हूं कि ls -R
निर्देशिकाओं की एक सूची प्रदर्शित करता है। लेकिन यह पुनरावर्ती क्यों है? प्रक्रिया में पुनरावृत्ति का उपयोग कैसे किया जाता है?
ls
एक निर्देशिका का सामना करता है तो वह उस निर्देशिका को पुन: सूचीबद्ध करता है।
मैं समझता हूं कि ls -R
निर्देशिकाओं की एक सूची प्रदर्शित करता है। लेकिन यह पुनरावर्ती क्यों है? प्रक्रिया में पुनरावृत्ति का उपयोग कैसे किया जाता है?
ls
एक निर्देशिका का सामना करता है तो वह उस निर्देशिका को पुन: सूचीबद्ध करता है।
जवाबों:
सबसे पहले, चलो एक मनमाना फ़ोल्डर संरचना परिभाषित करते हैं:
.
├── 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
}
}
}
और क्योंकि मैं कर सकता हूँ, एक संदर्भ जावा उसी का कार्यान्वयन ।
प्रभाव में, दो करीबी युग्मित प्रश्न जो आप पूछ रहे हैं।
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-dbgsym
gdb
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);
}
अन्यथा, फ़ंक्शन सिर्फ एक बार चलता है (कमांड लाइन पर निर्दिष्ट प्रति निर्देशिका)।
जब आप इसके बारे में सोचते हैं, "पुनरावर्ती" आदेशों और निर्देशिकाओं और उनकी फ़ाइलों और निर्देशिकाओं और उनकी फ़ाइलों और निर्देशिकाओं और उनकी फ़ाइलों और निर्देशिकाओं और उनकी फ़ाइलों पर कार्य करने के लिए समझ में आता है .........
.... जब तक निर्दिष्ट बिंदु से पूरे पेड़ को कमांड द्वारा संचालित नहीं किया गया है, इस मामले में किसी भी उपनिर्देशिका के किसी भी उपनिर्देशिका की सामग्री को सूचीबद्ध कर रहा है .......... जो कि नीचे मौजूद है तर्क (ओं) का आदेश
-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
प्लस बीच निर्देशिकाओं के बीच।
tree
हालांकि के लिए +1 । यह एक महान उपकरण है।
यहां एक सरल व्याख्या है, यह समझ में आता है क्योंकि जब उपनिर्देशिका की सामग्री प्रदर्शित करने की बात आती है, तो वही फ़ंक्शन पहले से ही जानता है कि एक निर्देशिका के साथ क्या करना है। इसलिए यह सिर्फ उस परिणाम को प्राप्त करने के लिए प्रत्येक उपनिर्देशिका पर खुद को कॉल करने की आवश्यकता है!
छद्मकोड में यह कुछ इस तरह दिखता है:
recursive_ls(dir)
print(files and directories)
foreach (directoriy in dir)
recursive_ls(directory)
end foreach
end recursive_ls