왜 찾을 수 없습니까? -delete 현재 디렉터리를 삭제하시겠습니까?

왜 찾을 수 없습니까? -delete 현재 디렉터리를 삭제하시겠습니까?

나는 희망

find . -delete

현재 디렉터리를 삭제하지만 삭제되지는 않습니다. 왜 안 돼?

답변1

회원은findutils 이것을 깨닫다, 이는 *BSD와의 호환성을 위한 것입니다.

"." 제거를 생략하는 이유 중 하나는 이 작업의 근원인 *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 에 도달합니다 unistd.h.

간단한 테스트로 점/점-점 매개변수가 있는 rmdir은 -1을 반환합니다.

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

보자POSIX가 rmdir을 설명하는 방법:

path 인수가 참조하는 경로의 마지막 구성 요소가 점 또는 점-점인 경우 rmdir()은 실패합니다.

이유는 제시되지 않았습니다 shall fail.

내가 찾은rename 몇 가지 이유를 설명하다숫자:

순환 파일 시스템 경로를 방지하려면 점 또는 점-점 이름 바꾸기를 비활성화합니다.

순환 파일 시스템 경로?

나는 살펴보았다C 프로그래밍 언어(2판)디렉토리 주제를 검색하다가 놀랍게도 발견했습니다.코드가 비슷해요:

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

댓글도 있어요!

각 디렉토리에는 항상 자체 항목("."이라고 함)과 상위 디렉토리 ".."가 포함되어 있으며 이를 건너뛰어야 합니다. 그렇지 않으면 프로그램이 실행됩니다.영원히 반복.

"영원한 루프", 이는 rename그것을 설명하는 것과 같은 방식 입니다"순환 파일 시스템 경로"이상.

코드를 약간 수정하여 Kali Linux 기반에서 작동하도록 만들었습니다.이 답변:

#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. GNU는 유틸리티 findutils와 호환되도록 시도합니다 .find*BSD.

  2. find*BSD의 유틸리티는 내부적으로 rmdirPOSIX 호환 C 함수를 사용하며 점/점-점은 허용되지 않습니다.

  3. rmdirdot/dot-dot이 허용되지 않는 이유는 순환 파일 시스템 경로를 방지하기 위한 것입니다.

  4. C 프로그래밍 언어K&R이 작성한 예제는 점/점-점이 어떻게 영원히 반복되는 프로그램으로 이어질 수 있는지 보여줍니다.

답변2

find명령이 결과를 반환하기 때문입니다 .. 정보 페이지에서 rm:

마지막 파일 이름 부분이 '.' 또는 '..'인 파일을 삭제하려는 시도는 POSIX에 지정된 대로 프롬프트 없이 거부됩니다.

find따라서 이 경우 POSIX 규칙을 따르는 것처럼 보입니다 .

답변3

"."rmdir 시스템 호출에 대한 인수 경로의 마지막 구성 요소가 이면 rmdir 시스템 호출이 실패하고 EINVAL을 반환합니다. 에 기록되어 있어요http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html 이 조치의 근거는 다음과 같습니다.

경로명 /dot 삭제의 의미는 삭제될 상위 디렉토리의 파일(디렉토리) 이름이 불분명하기 때문에 불분명합니다. 특히 디렉토리 링크가 여러 개 있는 경우에는 더욱 그렇습니다.

답변4

Lin Guohao와 Thomas가 이에 대해 좋은 답변을 했지만, 그들의 답변이 설명을 잊어버린 것 같습니다.이 행위가 먼저 수행되었습니다.

귀하의 예에서 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

아니요현재 디렉토리를 삭제하는 것은 최소한의 놀라움 원칙을 준수하는 좋은 인터페이스 디자인입니다.

관련 정보