
rm -rf my-symlink
これを使用するとシンボリック リンクのみが削除され、rm -rf my-symlink/
リンクされたディレクトリ内のファイルは削除されてシンボリック リンクはそのまま残るのはなぜか知りたいだけです。
答え1
stat my-symlink
との出力を比較すると違いがわかりますstat my-symlink/
。my-symlink
(スラッシュなし)はシンボリック リンク自体です。 (スラッシュありmy-symlink/
)はシンボリック リンクが指すディレクトリです。これは、シンボリック リンクが指すディレクトリの と の inode を比較することで個別に確認できますmy-symlink/
。
その情報を踏まえると、あなたが見ている行動は、rm
の仕様: シンボリック リンクを処理するときに、rm
リンクがディレクトリを指している場合は、そのリンクに「降りて」行かずにリンクを削除します。ディレクトリを処理する場合 (オプションを使用-r
)、その内容を再帰的に削除します。このmy-symlink/
場合、rm
ディレクトリを削除しようとしますが、ディレクトリではなくシンボリック リンクであるため失敗します。ただし、フラグがあるため、-f
エラーは発生しません。
答え2
この動作をもう少し詳しく調査してみようと思ったので、別の回答をここに示します。
rm
内部的には、FTファイル階層を再帰的に処理します。fts_open
パスの配列をパラメータとして受け取り、各パスのツリー構造を作成します。これにより、プログラマーは複数の場所を、あたかも 1 つの統合された階層の一部であるかのようにシームレスに探索できます。
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
もう一度 を反復処理します。この最後の 1 つは、FTS の動作によるものです。最初にコンテンツを反復処理し、次にルート ノードに戻ります。これは、 にとって非常に便利ですrm
。最初のパス ( D
) では、ファイルを消去し、2 番目のパス ( DP
) では、空のディレクトリを削除します。
ご覧のとおり、この場合、FTS はsym/
どちらの場合もディレクトリとして報告します。これは、パスの末尾にスラッシュを付けて指定したため、カーネルがそれをディレクトリとして解釈するからです。リンクの場合、これは何があってもそれをたどることを意味します。より技術的な用語で言えば、仕様には次のように書かれています。
少なくとも 1 つのスラッシュ以外の文字が含まれ、1 つ以上の末尾のスラッシュで終わるパス名は、パス名に 1 つのドット文字 (「.」) が追加されたものとして解決されます。
sym/
FTS はディレクトリとして報告するため、rm
空のディレクトリを削除するかのように動作します。したがって、 を呼び出しますunlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
。これにより、unlinkat
は のように動作しますrmdir
。
ただし、sym/
パスを解決すると、unlinkat
システム コールは実際にはディレクトリが指定されていないことを認識します。そのため、 が報告されENOTDIR
、次の処理が実行されます。
$ rm: cannot remove ‘sym/’: Not a directory
実際、-f
呼び出しからフラグを削除すると... まさにそれが表示されます。 さて、これがバグなのか機能なのか... 私にはわかりません。