
只是想知道為什麼如果我使用rm -rf my-symlink
它只會刪除符號鏈接,但rm -rf my-symlink/
會刪除鏈接目錄中的文件並保留符號鏈接?
答案1
stat my-symlink
透過比較和的輸出,您可以看到差異stat my-symlink/
。my-symlink
,不含斜杠,是符號鏈結本身;my-symlink/
帶斜線的 是符號連結指向的目錄,您可以透過比較my-symlink/
其指向的目錄的 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
) 中刪除空目錄。
如您所看到的,在本例中,FTSsym/
在這兩種情況下都會報告為目錄。這是因為我們為路徑添加了尾部斜杠,這強制核心將其解釋為目錄。對於連結來說,這意味著無論如何它都會跟隨它。從更技術的角度來說,規格說明:
至少包含一個非斜線字元且以一個或多個尾部斜線結尾的路徑名應像在路徑名中附加一個點字元 ( '.' ) 一樣進行解析。
由於 FTS 報告sym/
為目錄,rm
因此其行為就像刪除空目錄一樣。因此,它調用unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
.這會導致unlinkat
行為類似於rmdir
.
然而,在解析sym/
路徑後,unlinkat
系統呼叫將意識到實際上並沒有給它一個目錄。因此它將報告ENOTDIR
,這會觸發:
$ rm: cannot remove ‘sym/’: Not a directory
實際上,如果您-f
從呼叫中刪除該標誌...這正是您將看到的內容。現在,這是一個錯誤還是一個功能......我不知道。