를 찾지 못한 이유 -삭제 현재 디렉토리를 삭제 하시겠습니까?


22

나는 기대할 것이다

find . -delete

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


3
현재 작업 디렉토리를 제거하는 것은 좋지 않습니다.
Alexej Magura

동의 함-기본 동작이 마음에 들지만 예를 들어 find . -print.
mbroshi

@AlexejMagura 비록 동정했지만 현재 디렉토리를 제거하는 것이 열린 파일을 제거하는 것과 다른 이유를 알 수 없습니다. 객체는 객체에 대한 참조가 존재할 때까지 활성 상태를 유지 한 다음 나중에 가비지 수집됩니다. cd ..; rm -r dir의미가 명확한 다른 쉘을 사용할 수 있습니다 .
Rmano

@Rmano 이것은 사실입니다 : 그것은 원칙적으로하지 않을 것입니다 : 디렉토리를 올라가서 현재 디렉토리를 삭제하십시오. 상대 경로가 더 이상 작동하지 않는 등 현재 디렉토리에 더 이상 존재하지 않지만 절대 경로를 사용하여 항상 벗어날 수는 있지만 왜 그렇게 큰 일인지 확실하지 않습니다. 내 일부는 단지 좋은 생각이 아니라고 말합니다.
Alexej Magura

답변:


29

findutils 그것을 알고 있는 멤버는 * BSD와 호환됩니다.

"."삭제를 건너 뛰는 이유 중 하나입니다. 이 동작이 시작된 * BSD와의 호환성을위한 것입니다.

findutils 소스 코드 의 NEWS 는 동작을 유지하기로 결정했음을 보여줍니다.

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

[최신 정보]

이 질문은 뜨거운 주제 중 하나가되었으므로 FreeBSD 소스 코드를 살펴보고 더 설득력있는 이유를 제시합니다.

FreeBSDfind 유틸리티 소스 코드를 보자 :

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 's에 의해 정의 된 C 함수에 도달 unistd.h합니다.

간단한 테스트를 수행하십시오 .dot / dot-dot 인수가있는 rmdir은 -1을 반환합니다.

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

POSIX가 rmdir을 어떻게 설명하는지 살펴 보자 .

path 인수가 최종 구성 요소가 dot 또는 dot-dot 인 경로를 참조하면 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 는 * BSD의 유틸리티 findutils와 호환을 시도합니다 .find

  2. find* BSD의 유틸리티는 내부적 rmdir으로 dot / dot-dot가 허용되지 않는 POSIX 호환 C 함수를 사용합니다.

  3. rmdir도트 / 도트를 허용하지 않는 이유는 주기적 파일 시스템 경로를 방지하기위한 것입니다.

  4. K & R이 작성한 C 프로그래밍 언어 는 도트 / 도트가 어떻게 영원히 루프 프로그램을 만드는지를 보여줍니다.


16

당신 때문에 find명령이 리턴 .결과. 의 정보 페이지에서 rm:

마지막 파일 이름 구성 요소가 '.'인 파일을 제거하려고 시도했습니다. 또는 POSIX에서 지시 한대로 프롬프트없이 '..'이 거부됩니다.

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


2
POSIX는 킹이며 현재 디렉토리를 제거하면 부모 응용 프로그램에 따라 매우 큰 문제가 발생할 수 있습니다. 현재 디렉토리가 /var/log있고 루트로 실행하면 모든 하위 디렉토리가 제거되고 현재 디렉토리도 제거되었다고 생각하면 어떻게됩니까?
Alexej Magura

1
즉, 좋은 이론,하지만 man페이지는 find말한다 : "제거 오류 메시지가 발행되고, 실패하면." 왜 오류가 인쇄되지 않습니까?
mbroshi

1
@AlexejMagura 현재 디렉토리를 제거하면 일반적으로 잘 작동합니다 : mkdir foo && cd foo && rmdir $(pwd). 작동하지 않는 .(또는 ..) 제거 중 입니다.
Tavian Barnes

4

인수 경로의 마지막 구성 요소가 인 경우 rmdir 시스템 호출은 EINVAL과 함께 실패합니다 ".". http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html에 문서화되어 있으며 동작의 근거는 다음과 같습니다.

제거 할 상위 디렉토리의 파일 (디렉토리) 이름, 특히 디렉토리에 대한 여러 개의 링크가있는 경우 경로 이름 / dot를 삭제하는 의미는 명확하지 않습니다.


2

호출 rmdir(".")내가 그것을 시도 할 때 시스템 콜은 그래서 아무 높은 수준의 도구가 성공할 수, 작동하지 않았다한다.

.별명이 아닌 실제 이름을 통해 디렉토리를 삭제해야합니다 .


1

林果 皞와 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

현재 디렉토리를 삭제 하지 않는 것은 단순히 훌륭한 인터페이스 디자인이며 최소한의 놀람 원칙을 준수합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.