क्यों नहीं मिल रहा है? वर्तमान निर्देशिका हटाएं?


22

मुझे अपेक्षा होगी

find . -delete

वर्तमान निर्देशिका को हटाने के लिए, लेकिन यह नहीं है। क्यों नहीं?


3
सबसे अधिक संभावना है क्योंकि वर्तमान कामकाजी निर्देशिका को हटाना एक अच्छा विचार नहीं होगा।
अलेक्सज मगुरा

सहमत - मुझे डिफ़ॉल्ट व्यवहार पसंद है, लेकिन यह उदाहरण के अनुरूप नहीं है find . -print
मब्रोशी

@AlexejMagura यद्यपि मुझे सहानुभूति है, मैं यह नहीं देखता कि वर्तमान निर्देशिका को हटाना किसी खुली फ़ाइल को हटाने से अलग क्यों होना चाहिए। ऑब्जेक्ट तब तक जीवित रहेगा जब तक कि उसका कोई संदर्भ मौजूद न हो, और उसके बाद कचरा एकत्रित हो जाए। आप cd ..; rm -r dirएक अन्य शेल के साथ काफी स्पष्ट शब्दार्थों के साथ कर सकते हैं ...
रमनो

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

जवाबों:


29

सदस्यों के findutils बारे में पता है , यह * BSD के साथ संगत के लिए है:

कारणों में से एक है कि हम "" को हटाना छोड़ देते हैं। * बीएसडी के साथ अनुकूलता के लिए, जहां यह कार्रवाई हुई।

समाचार findutils स्रोत कोड शो में है कि वे व्यवहार रखने का फैसला किया:

#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".

[अद्यतन करें]

चूंकि यह प्रश्न गर्म विषय में से एक बन गया है, इसलिए मैं FreeBSD स्रोत कोड में गोता लगाता हूं और अधिक ठोस कारण बताता हूं।

आइए FreeBSD का यूटिलिटी सोर्स कोड देखें :

int
f_delete(PLAN *plan __unused, FTSENT *entry)
{
    /* ignore these from fts */
    if (strcmp(entry->fts_accpath, ".") == 0 ||
        strcmp(entry->fts_accpath, "..") == 0)
        return 1;
...
    /* rmdir directories, unlink everything else */
    if (S_ISDIR(entry->fts_statp->st_mode)) {
        if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
            warn("-delete: rmdir(%s)", entry->fts_path);
    } else {
        if (unlink(entry->fts_accpath) < 0)
            warn("-delete: unlink(%s)", entry->fts_path);
    }
...

जैसा कि आप देख सकते हैं, अगर यह डॉट और डॉट-डॉट को फ़िल्टर नहीं करता है, तो यह rmdir()POSIX द्वारा परिभाषित C फ़ंक्शन तक पहुंच जाएगा unistd.h

एक साधारण परीक्षण करें, डॉट / डॉट-डॉट तर्क के साथ rmdir -1 लौट आएगा:

printf("%d\n", rmdir(".."));

आइए एक नज़र डालते हैं कि POSIX rmdir का वर्णन कैसे करते हैं :

यदि पथ तर्क उस पथ को संदर्भित करता है जिसका अंतिम घटक या तो डॉट या डॉट-डॉट है, तो rmdir () विफल हो जाएगा।

कोई कारण नहीं बताया गया shall fail

मैंने पाया rename कि कुछ reaso n समझाएँ :

चक्रीय फ़ाइल सिस्टम पथ को रोकने के लिए नाम या डॉट-डॉट का नामकरण निषिद्ध है।

चक्रीय फ़ाइल सिस्टम पथ ?

मैं सी प्रोग्रामिंग लैंग्वेज (2 डी संस्करण) को देखता हूं और निर्देशिका विषय की खोज करता हूं, आश्चर्यजनक रूप से मैंने पाया कि कोड समान है :

if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
    continue;

और टिप्पणी!

प्रत्येक निर्देशिका में हमेशा "," और उसके माता-पिता, ".." नामक प्रविष्टियाँ होती हैं; इन्हें छोड़ दिया जाना चाहिए, या प्रोग्राम हमेशा के लिए लूप हो जाएगा

"हमेशा के लिए लूप" , यह ऊपर दिए गए "चक्रीय फ़ाइल सिस्टम पथ" केrename रूप में वर्णन करने के तरीके के समान है।

मैं कोड को थोड़ा संशोधित करता हूं और इस उत्तर के आधार पर काली लिनक्स में इसे चलाने के लिए :

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

void fsize(char *);
void dirwalk(char *, void (*fcn)(char *));

int
main(int argc, char **argv) {
    if (argc == 1)
        fsize(".");
    else
        while (--argc > 0) {
            printf("start\n");
            fsize(*++argv);
        }
    return 0;
}

void fsize(char *name) {
    struct stat stbuf;
    if (stat(name, &stbuf) == -1 )  {
        fprintf(stderr, "fsize: can't access %s\n", name);
        return;
    }
    if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
        dirwalk(name, fsize);
    printf("%81d %s\n", stbuf.st_size, name);
}

#define MAX_PATH 1024
void dirwalk(char *dir, void (*fcn)(char *))
{
    char name[MAX_PATH];
    struct dirent *dp;

    DIR *dfd;

    if ((dfd = opendir(dir)) == NULL) {
            fprintf(stderr, "dirwalk: can't open %s\n", dir);
            return;
    }

    while ((dp = readdir(dfd)) != NULL) {
            sleep(1);
            printf("d_name: S%sG\n", dp->d_name);
            if (strcmp(dp->d_name, ".") == 0
                            || strcmp(dp->d_name, "..") == 0) {
                    printf("hole dot\n");
                    continue;
                    }
            if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name)) {
                    printf("mocha\n");
                    fprintf(stderr, "dirwalk: name %s/%s too long\n",
                                    dir, dp->d_name);
                    }
            else {
                    printf("ice\n");
                    (*fcn)(dp->d_name);
            }
    }
    closedir(dfd);
}

चलो देखते हैं:

xb@dnxb:/test/dot$ ls -la
total 8
drwxr-xr-x 2 xiaobai xiaobai 4096 Nov 20 04:14 .
drwxr-xr-x 3 xiaobai xiaobai 4096 Nov 20 04:14 ..
xb@dnxb:/test/dot$ 
xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out 
xb@dnxb:/test/dot$ /tmp/kr/a.out .                     
start
d_name: S..G
hole dot
d_name: S.G
hole dot
                                                                             4096 .
xb@dnxb:/test/dot$ 

यह सही ढंग से काम करता है, अब क्या होगा अगर मैं continueनिर्देश पर टिप्पणी करता हूं :

xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out 
xb@dnxb:/test/dot$ /tmp/kr/a.out .
start
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
^C
xb@dnxb:/test/dot$

जैसा कि आप देख सकते हैं, मुझे इस असीम लूप प्रोग्राम को मारने के लिए Ctrl+ Cका उपयोग करना होगा।

'..' निर्देशिका ने अपनी पहली प्रविष्टि '..' और लूप को हमेशा के लिए पढ़ा।

निष्कर्ष:

  1. जीएनयू * बीएसडी में उपयोगिता findutilsके साथ संगत करने का प्रयास करता है ।find

  2. find* बीएसडी में उपयोगिता आंतरिक रूप से rmdirPOSIX- अनुरूप C फ़ंक्शन का उपयोग करती है जो डॉट / डॉट-डॉट की अनुमति नहीं है।

  3. rmdirडॉट / डॉट-डॉट की अनुमति नहीं देने का कारण चक्रीय फ़ाइल सिस्टम पथ को रोकना है।

  4. K & R द्वारा लिखित C प्रोग्रामिंग लैंग्वेज इस बात का उदाहरण दिखाती है कि कैसे डॉट / डॉट-डॉट हमेशा के लिए लूप प्रोग्राम को बढ़ावा देगा।


16

क्योंकि आपका findकमांड .परिणाम के रूप में लौटता है । के जानकारी पृष्ठ से rm:

किसी फ़ाइल को निकालने का कोई प्रयास जिसका अंतिम फ़ाइल नाम घटक है '।' या '..' को बिना किसी संकेत के अस्वीकार कर दिया गया है, जैसा कि POSIX द्वारा अनिवार्य है।

तो, ऐसा लगता है कि findइस मामले में बस पोसिक्स के नियमों से चिपक जाता है।


2
जैसा कि यह होना चाहिए: पोसिक्स राजा है, साथ ही वर्तमान निर्देशिका को हटाने से मूल आवेदन के आधार पर कुछ बहुत बड़ी समस्याएं पैदा हो सकती हैं और क्या नहीं। क्या होगा यदि वर्तमान निर्देशिका थी /var/logऔर आप उस रूट को सोच रहे थे, यह सोचकर कि यह सभी उपखंडों को हटा देगा और वर्तमान निर्देशिका को भी हटा देगा?
अलेक्सज मगुरा

1
यह एक अच्छा सिद्धांत है, लेकिन इसके लिए manपृष्ठ findकहता है: "यदि निष्कासन विफल हुआ, तो एक त्रुटि संदेश जारी किया जाता है।" कोई त्रुटि क्यों नहीं छपी है?
मब्रोशी

1
@AlexejMagura वर्तमान निर्देशिका को निकालना सामान्य रूप से ठीक काम करता है mkdir foo && cd foo && rmdir $(pwd):। यह काम नहीं कर रहा है .(या ..) निकाल रहा है।
तेवियन बार्न्स

4

Rmdir सिस्टम कॉल EINVAL के साथ विफल हो जाती है यदि इसके तर्क पथ का अंतिम घटक है "."। यह http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html पर प्रलेखित है और व्यवहार के लिए तर्क है:

Pathname / dot को हटाने का अर्थ स्पष्ट नहीं है, क्योंकि हटाए जाने के लिए मूल निर्देशिका में फ़ाइल (निर्देशिका) का नाम स्पष्ट नहीं है, विशेष रूप से एक निर्देशिका के लिए कई लिंक की उपस्थिति में।


2

कॉलिंग rmdir(".")जब मैं इसे करने की कोशिश एक सिस्टम कॉल के रूप में काम नहीं किया है, इसलिए कोई उच्च स्तर उपकरण सफल होने के कर सकते हैं।

आपको निर्देशिका को उसके वास्तविक नाम के माध्यम से हटाना होगा न कि उसके .उपनाम से।


1

जबकि While, और थॉमस ने पहले ही इस पर अच्छे उत्तर दे दिए थे, मुझे लगता है कि उनके उत्तर यह बताना भूल गए कि इस व्यवहार को पहले स्थान पर क्यों लागू किया गया था।

आपके find . -deleteउदाहरण में वर्तमान निर्देशिका को हटाना बहुत तार्किक और समझदार लगता है। पर विचार करें:

$ find . -name marti\*
./martin
./martin.jpg
[..]

क्या आप .अभी भी तार्किक और समझदार हैं?

एक गैर-रिक्त निर्देशिका को हटाना एक त्रुटि है - इसलिए आप इसके साथ डेटा को खोने की संभावना नहीं रखते हैं find(हालांकि आप के साथ कर सकते हैं rm -r) - लेकिन आपके शेल में इसकी वर्तमान कार्यशील निर्देशिका एक निर्देशिका के लिए सेट होगी जो अब मौजूद नहीं है, जिससे कुछ भ्रमित हो सकता है और आश्चर्यजनक व्यवहार:

$ pwd
/home/martin/test
$ rm -r ../test 
$ touch foo
touch: cannot touch 'foo': No such file or directory

नहीं वर्तमान निर्देशिका हटाने का अच्छा इंटरफेस डिजाइन और कम से कम आश्चर्य की बात है के सिद्धांत का अनुपालन।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.