これを使用すると、なぜ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
)ではファイルを削除でき、2番目のパス(DP
)では空のディレクトリを削除します。
ご覧のとおり、この例では、FTSはsym/
どちらの場合もディレクトリとして報告します。これは、パスに末尾のスラッシュを追加してカーネルがそれをディレクトリとして解釈できるようにするためです。リンクの場合、これは何があってもリンクをたどるという意味です。より技術的な観点から見ると、仕様には次のような内容が示されています。
1つ以上のスラッシュ以外の文字を含み、1つ以上の末尾のスラッシュで終わるパス名は、ドット文字( '.')がパス名に追加されたかのように解析されます。
sym/
FTSはディレクトリとして報告するため、rm
空のディレクトリが削除されたかのように動作します。したがって、unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
unlinkat
rmdir
ただし、sym/
パスを確認した後、unlinkat
システムコールは実際にディレクトリが提供されていないことを認識します。したがって、ENOTDIR
以下をトリガーする報告が発生します。
$ rm: cannot remove ‘sym/’: Not a directory
実際に-f
通話からそのフラグを削除すると...これがすぐに表示される内容です。さて、これがバグなのか機能なのか…わかりません。