rm my-symlink と rm my-symlink/ の違い

rm my-symlink と rm my-symlink/ の違い

これを使用すると、なぜ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

この動作をさらに調査する必要があると思ったので、ここに別の答えがあります。

内部的にrmFTSファイル階層に再帰します。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)unlinkatrmdir

ただし、sym/パスを確認した後、unlinkatシステムコールは実際にディレクトリが提供されていないことを認識します。したがって、ENOTDIR以下をトリガーする報告が発生します。

$ rm: cannot remove ‘sym/’: Not a directory

実際に-f通話からそのフラグを削除すると...これがすぐに表示される内容です。さて、これがバグなのか機能なのか…わかりません。

関連情報