web-dev-qa-db-ja.com

が見つからないのはなぜですか。 -delete現在のディレクトリを削除しますか?

私は期待します

find . -delete

現在のディレクトリを削除しますが、削除しません。何故なの?

22
mbroshi

findutilsのメンバー それを認識 、* BSDとの互換性のために:

「。」の削除をスキップする理由の一つ。このアクションが発生した* BSDとの互換性のためです。

Findutilsソースコードの[〜#〜] news [〜#〜]は、動作を維持することを決定したことを示しています。

_#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete "."._

[UPDATE]

この質問はホットな話題の1つになるので、FreeBSDのソースコードに飛び込んで、より説得力のある理由を明らかにします。

FreeBSDのユーティリティソースコードを探す を見てみましょう:

_int
f_delete(PLAN *plan __unused, FTSENT *entry)
{
    /* ignore these from fts */
    if (strcmp(entry->fts_accpath, ".") == 0 ||
        strcmp(entry->fts_accpath, "..") == 0)
        return 1;
...
    /* rmdir directories, unlink everything else */
    if (S_ISDIR(entry->fts_statp->st_mode)) {
        if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
            warn("-delete: rmdir(%s)", entry->fts_path);
    } else {
        if (unlink(entry->fts_accpath) < 0)
            warn("-delete: unlink(%s)", entry->fts_path);
    }
...
_

ご覧のように、ドットとドットドットをフィルターで除去しない場合、POSIXの_unistd.h_で定義されているrmdir() C関数に到達します。

簡単なテストを行います。ドット/ドット-ドット引数を指定したrmdirは、-1を返します。

_printf("%d\n", rmdir(".."));
_

見てみましょう POSIXによるrmdirの記述方法

Path引数が最終コンポーネントがドットまたはドット-ドットのいずれかであるパスを参照している場合、rmdir()は失敗します。

_shall fail_の理由は示されていません。

renameが見つかりました 理由を説明してください n:

循環ファイルシステムパスを防止するために、ドットまたはドットドットの名前を変更することは禁止されています。

循環ファイルシステムパス

私はCプログラミング言語(第2版)を調べてディレクトリトピックを検索しますが、驚くべきことに コードは似ています

_if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
    continue;
_

そしてコメント!

各ディレクトリには常に「。」と呼ばれるそれ自体のエントリとその親「..」が含まれます。これらはスキップする必要があります。そうしないと、プログラムは永久にループします

"永久にループ"、これはrename "循環ファイルシステムとしてそれを記述する方法と同じです上記のパス "

私はコードを少し変更して、 この答え に基づいてKali Linuxで実行できるようにします:

_#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h> 
#include <dirent.h>
#include <unistd.h>

void fsize(char *);
void dirwalk(char *, void (*fcn)(char *));

int
main(int argc, char **argv) {
    if (argc == 1)
        fsize(".");
    else
        while (--argc > 0) {
            printf("start\n");
            fsize(*++argv);
        }
    return 0;
}

void fsize(char *name) {
    struct stat stbuf;
    if (stat(name, &stbuf) == -1 )  {
        fprintf(stderr, "fsize: can't access %s\n", name);
        return;
    }
    if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
        dirwalk(name, fsize);
    printf("%81d %s\n", stbuf.st_size, name);
}

#define MAX_PATH 1024
void dirwalk(char *dir, void (*fcn)(char *))
{
    char name[MAX_PATH];
    struct dirent *dp;

    DIR *dfd;

    if ((dfd = opendir(dir)) == NULL) {
            fprintf(stderr, "dirwalk: can't open %s\n", dir);
            return;
    }

    while ((dp = readdir(dfd)) != NULL) {
            sleep(1);
            printf("d_name: S%sG\n", dp->d_name);
            if (strcmp(dp->d_name, ".") == 0
                            || strcmp(dp->d_name, "..") == 0) {
                    printf("hole dot\n");
                    continue;
                    }
            if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name)) {
                    printf("mocha\n");
                    fprintf(stderr, "dirwalk: name %s/%s too long\n",
                                    dir, dp->d_name);
                    }
            else {
                    printf("ice\n");
                    (*fcn)(dp->d_name);
            }
    }
    closedir(dfd);
}
_

どれどれ:

_xb@dnxb:/test/dot$ ls -la
total 8
drwxr-xr-x 2 xiaobai xiaobai 4096 Nov 20 04:14 .
drwxr-xr-x 3 xiaobai xiaobai 4096 Nov 20 04:14 ..
xb@dnxb:/test/dot$ 
xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out 
xb@dnxb:/test/dot$ /tmp/kr/a.out .                     
start
d_name: S..G
hole dot
d_name: S.G
hole dot
                                                                             4096 .
xb@dnxb:/test/dot$ 
_

正しく機能しますが、continue命令をコメントアウトするとどうなるでしょうか。

_xb@dnxb:/test/dot$ cc /tmp/kr/fsize.c -o /tmp/kr/a.out 
xb@dnxb:/test/dot$ /tmp/kr/a.out .
start
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
d_name: S..G
hole dot
ice
^C
xb@dnxb:/test/dot$
_

ご覧のとおり、私は Ctrl+C この無限ループプログラムを強制終了します。

「..」ディレクトリは最初のエントリ「..」を読み取り、永久にループします。

結論:

  1. GNU findutils* BSDfindユーティリティとの互換性を試します。

  2. * BSDのfindユーティリティは、ドット/ドット-ドットが許可されていないrmdir POSIX準拠のC関数を内部的に使用します。

  3. rmdirがドット/ドットドットを許可しない理由は、循環ファイルシステムパスを防ぐためです。

  4. K&Rによって作成されたCプログラミング言語は、ドット/ドット-ドットが永久ループプログラムにつながる方法の例を示しています。

29
林果皞

findコマンドは結果として.を返すためです。 rmの情報ページから:

最後のファイル名コンポーネントが「。」または「..」であるファイルを削除しようとすると、POSIXで義務付けられているように、プロンプトなしで拒否されます。

したがって、この場合、findはPOSIXルールに固執するように見えます。

16
Thomas

引数パスの最後のコンポーネントが"."の場合、rmdirシステムコールはEINVALで失敗します。 http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html に記載されており、動作の根拠は次のとおりです。

特にディレクトリへの複数のリンクが存在する場合、削除する親ディレクトリ内のファイル(ディレクトリ)の名前が明確でないため、パス名/ dotを削除する意味は不明です。

4
PSkocik

システムコールとしてrmdir(".")を呼び出すと、試しても機能しなかったため、上位レベルのツールは成功しません。

.エイリアスではなく、実際の名前でディレクトリを削除する必要があります。

2
Joshua

林果皞とトーマスはすでにこれについて良い答えを出しましたが、私は彼らの答えが説明するのを忘れていたように思いますwhyこの動作は最初に実装されました。

あなたのfind . -delete現在のディレクトリを削除する例は、かなり論理的で正気に聞こえます。しかし考慮してください:

$ find . -name marti\*
./martin
./martin.jpg
[..]

削除します.まだ論理的で正気に聞こえますか?

空でないディレクトリを削除するとエラーになります– findを使用すると、これでデータが失われる可能性はほとんどありません(rm -r)–ただし、シェルでは現在の作業ディレクトリが存在しないディレクトリに設定されるため、混乱を招き、驚くべき動作が発生します。

$ pwd
/home/martin/test
$ rm -r ../test 
$ touch foo
touch: cannot touch 'foo': No such file or directory

Not現在のディレクトリを削除することは、単に優れたインターフェース設計であり、驚きを最小限に抑えるという原則に準拠しています。

1
Martin Tournoij