이것을 사용하면 왜 rm -rf my-symlink
심볼릭 링크만 제거되고 rm -rf my-symlink/
링크된 디렉터리의 파일은 삭제되고 심볼릭 링크는 유지되는지 궁금합니다.
답변1
stat my-symlink
와 의 출력을 비교하면 차이점을 확인할 수 있습니다 stat my-symlink/
. my-symlink
슬래시가 없는 기호 링크 자체는 my-symlink/
기호 링크가 가리키는 디렉터리이며, 기호 링크가 my-symlink/
가리키는 디렉터리의 inode와 inode를 비교하여 독립적으로 확인할 수 있습니다.
이 정보를 사용하면 표시되는 동작이 에 설명된 것과 일치합니다.rm
명세서: 기호 링크를 처리할 때 rm
링크가 디렉토리를 가리키는 경우 "아래로" 이동하지 않고 링크를 삭제합니다. 디렉토리를 처리할 때(옵션 사용 -r
) 해당 내용을 재귀적으로 삭제합니다. 이 my-symlink/
경우 rm
"디렉터리"를 삭제하려고 시도하지만 디렉터리가 아니라 심볼릭 링크이기 때문에 실패합니다. 그러나 -f
플래그로 인해 오류가 발생하지는 않습니다.
답변2
이 동작을 더 조사해야 한다고 생각했기 때문에 여기에 또 다른 답변이 있습니다.
내부적으로 rm
는FTS파일 계층 구조로 재귀합니다. fts_open
경로 배열을 매개변수로 사용하고 각 경로에 대한 트리 구조를 만듭니다. 이를 통해 프로그래머는 마치 통합 계층의 일부인 것처럼 여러 위치를 원활하게 탐색할 수 있습니다.
FTS를 직접 플레이해볼 수 있는 테스트 프로그램입니다.
#include <stdio.h>
#include <stdlib.h>
#include <fts.h>
int main(int argc, char* argv[])
{
if(argc < 2) return EXIT_FAILURE;
char* const* arr = argv + 1;
FTS* hier = fts_open(arr, FTS_NOSTAT | FTS_PHYSICAL, NULL);
FTSENT* ent;
while((ent = fts_read(hier))) {
printf("%s info=%d (D=%d DP=%d F=%d SL=%d)\n",
ent->fts_accpath, ent->fts_info,
ent->fts_info == FTS_D, ent->fts_info == FTS_DP,
ent->fts_info == FTS_F || ent->fts_info == FTS_NSOK,
ent->fts_info == FTS_SL);
}
fts_close(hier);
return EXIT_SUCCESS;
}
다음과 같은 디렉터리 구조를 생성했다고 가정합니다.
$ mkdir dir
$ touch dir/file
$ ln -s dir sym
이제 첫 번째 사례를 고려하여 FTS가 탐색을 어떻게 주도하는지 살펴보겠습니다.
$ gcc fts.c
$ ./a.out sym
sym info=12 (D=0 DP=0 F=0 SL=1)
보시다시피 이 경우에는 sym
파일로 처리됩니다. 좀 더 정확하게 말하면 심볼릭 링크입니다. 이 정보를 사용하여 rm
파일로 처리하고 을 호출할 수 있습니다 unlinkat(AT_FDCWD, "sym", 0)
. 마지막 인수(0)는 unlinkat
와 유사한 동작을 발생시킵니다 unlink
. 즉, 파일을 삭제하는 것뿐입니다. 결과적으로 링크가 사라집니다.
이제 무슨 일이 일어나는지 봅시다 sym/
.
$ ./a.out sym/
sym/ info=1 (D=1 DP=0 F=0 SL=0)
file info=11 (D=0 DP=0 F=1 SL=0)
sym/ info=6 (D=0 DP=1 F=0 SL=0)
이 경우 sym
대상 디렉터리로 간주됩니다. 먼저 반복 sym
한 다음 sym/file
다시 반복합니다 sym
. 마지막은 FTS 작동 방식 때문입니다. 먼저 내용을 반복한 다음 루트 노드를 반환합니다. 이것은 실제로 당신에게 매우 편리합니다 rm
. 첫 번째 패스( D
)에서는 파일을 삭제할 수 있고 두 번째 패스( DP
)에서는 빈 디렉터리를 삭제합니다.
보시다시피, 이 예에서 FTS는 sym/
두 경우 모두 디렉터리로 보고합니다. 이는 경로에 후행 슬래시를 추가하여 커널이 이를 디렉터리로 해석하도록 하기 때문입니다. 링크의 경우 이는 무슨 일이 있어도 링크를 따라간다는 의미입니다.좀 더 기술적인 관점에서 보면 사양에는 다음과 같은 내용이 나와 있습니다.
하나 이상의 슬래시가 아닌 문자를 포함하고 하나 이상의 후행 슬래시로 끝나는 경로 이름은 마치 점 문자( '.' )가 경로 이름에 추가된 것처럼 구문 분석됩니다.
sym/
FTS는 디렉터리로 보고하므로 rm
마치 빈 디렉터리가 삭제된 것처럼 동작합니다. 따라서 unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
.unlinkat
rmdir
그러나 sym/
경로를 확인한 후 unlinkat
시스템 호출은 실제로 디렉터리가 제공되지 않았음을 인식합니다. 따라서 ENOTDIR
다음을 트리거하는 보고가 발생합니다.
$ rm: cannot remove ‘sym/’: Not a directory
실제로 -f
통화에서 해당 플래그를 제거하면... 이것이 바로 표시되는 내용입니다. 자, 이것이 버그인지 기능인지... 모르겠습니다.