Neden bulamıyor -delete geçerli dizini sil?


22

Beklemek isterim

find . -delete

Geçerli dizini silmek için, ancak değil. Neden olmasın?


3
Büyük olasılıkla mevcut çalışma dizinini kaldırmak iyi bir fikir olmaz.
Alexej Magura

Kabul edildi - Varsayılan davranışı seviyorum, ancak bu, örneğin, ile tutarlı değil find . -print.
mbroshi

@AlexejMagura, sempati duymama rağmen, geçerli dizini kaldırmanın neden açık bir dosyayı kaldırmaktan farklı olduğunu anlamıyorum. Nesne, bir başvuru var olana kadar hayatta kalacak ve daha sonra çöp toplanacaktır. cd ..; rm -r dirOldukça net bir anlambilim ile başka bir kabuk ile yapabilirsiniz ...
Rmano

@Rmano bu doğru: sadece prensipte yapamayacağım bir şey: sadece bir dizine gidip mevcut dizini sil. Neden bu kadar önemli bir şey olduğundan tam olarak emin değilim - şu anki dizinde artık mevcut olmayan bazı talihsizlikler yaşamaya rağmen, göreceli yollar artık çalışmıyor, ancak her zaman mutlak bir yol kullanarak çıkabilirsiniz - ama bir parçam sadece bunun genel olarak iyi bir fikir olmadığını söylüyor.
Alexej Magura

Yanıtlar:


29

findutils Farkında olan üyeleri, * BSD ile uyumlu için:

"" Silme işleminin atlanmasının sebeplerinden biri. bu eylemin kaynaklandığı * BSD ile uyumluluk içindir.

HABER onlar davranışlarını tutmaya karar findutils kaynak kod gösterir:

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

[GÜNCELLEŞTİRME]

Bu soru sıcak konulardan biri olduğu için FreeBSD kaynak koduna daldım ve daha inandırıcı bir neden buldum.

FreeBSD'nin find yardımcı programı kaynak kodunu görelim :

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);
    }
...

Gördüğünüz gibi, nokta ve nokta noktasını filtrelemezse, POSIX'in rmdir()tanımladığı C işlevine erişir unistd.h.

Basit bir test yapın, rmdir ile dot / dot-dot argümanı -1 döndürür:

printf("%d\n", rmdir(".."));

POSIX'in rmdir'i nasıl tanımladığına bir bakalım :

Path argümanı, son bileşeni nokta ya da nokta-nokta olan bir yola atıfta bulunursa, rmdir () başarısız olur.

Sebebi neden yoktu shall fail.

Bulduğum rename bazı reaso açıklamak n:

Döngüsel dosya sistemi yollarını önlemek için nokta veya nokta noktanın yeniden adlandırılması yasaktır.

Döngüsel dosya sistemi yolları ?

C Programlama Dili'ne (2. Baskı) baktım ve dizin konusunu araştırdım, şaşırtıcı bir şekilde kodun benzer olduğunu gördüm :

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

Ve yorum!

Her dizin daima kendisi için "." Adı verilen girişleri ve onun üst öğesi ".."; bunlar atlanmalıdır, aksi takdirde program sonsuza kadar döngülenir .

"sonsuza kadar döngü" , bu nasıl gibi aynıdır renameolarak tarif "döngüsel dosya sistemi yolları" yukarıda.

Kodu biraz değiştirip bu cevabı temel alarak Kali Linux'ta çalışmasını sağlamak için :

#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);
}

Bakalım:

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$ 

Doğru çalışıyor, şimdi continuetalimatı yorumlasam ne olur :

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$

Gördüğünüz gibi, bu sonsuz döngüsel programı öldürmek için Ctrl+ kullanmam Cgerekiyor.

'..' dizini ilk girişi olan '..' 'ı okur ve sonsuza dek tekrar eder.

Sonuç:

  1. GNU * BSD'deki yardımcı programla findutilsuyumlu olmaya çalışır .find

  2. find* BSD'deki yardımcı program dahili olarak rmdirnokta / nokta-noktaya izin verilmeyen POSIX uyumlu C işlevini kullanır .

  3. rmdirNokta / nokta-noktaya izin vermeme nedeni döngüsel dosya sistemi yollarını önlemektir.

  4. K&R tarafından yazılan C Programlama Dili , nokta / nokta-noktanın sonsuza dek döngü programına nasıl gideceği örneğini gösterir.


16

Çünkü findemriniz .sonuç olarak döner . Bilgi sayfasından rm:

Son dosya adı bileşeni '.' Olan bir dosyayı kaldırma girişimi. veya '..' POSIX tarafından zorunlu tutulduğu şekilde herhangi bir talepte bulunulmaksızın reddedilir.

Yani, findbu durumda sadece POSIX kurallarına sadık kalıyor gibi görünüyor .


2
Olması gerektiği gibi: POSIX kraldır, artı mevcut dizini kaldırmak, ana uygulamaya ve ne olacağına bağlı olarak çok büyük sorunlara neden olabilir. Mesela eğer mevcut dizin olsaydı /var/logve siz onu kök olarak koştuysanız, tüm alt dizinleri sileceğini ve mevcut dizini de kaldırdığını düşünerek?
Alexej Magura

1
Bu iyi bir teori, ancak şu mansayfada findyazıyor: "Kaldırma işlemi başarısız olursa bir hata mesajı verilir." Neden hata yazdırılmıyor?
mbroshi

1
@AlexejMagura Geçerli dizini çıkarma genel olarak ceza çalışır: mkdir foo && cd foo && rmdir $(pwd). Çalışmıyor kaldırma .(veya ..).
Tavian Barnes


2

Denemek rmdir(".")istediğimde sistem çağrısı yapmak işe yaramadı;

Dizini, .takma adını değil gerçek adıyla silmelisiniz .


1

皞 皞 ve Thomas zaten bu konuda iyi cevaplar verirken, cevaplarının bu davranışın neden ilk başta uygulandığını açıklamayı unuttuğunu hissediyorum .

Örnekte find . -delete, geçerli dizini silmek oldukça mantıklı ve aklı başında geliyor. Ancak şunu düşünün:

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

Silme .hala mantıklı ve aklı başında geliyor mu?

Boş olmayan bir dizini silmek bir hatadır - bu nedenle find(bununla birlikte rm -r) bununla veri kaybetme ihtimaliniz yoktur - ancak kabuğunuzun mevcut çalışma dizini artık mevcut olmayan bir dizine ayarlanmış ve kafa karıştırıcı olabilir. ve şaşırtıcı davranış:

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

Değil Geçerli dizini silme sadece az sürpriz prensibi ile iyi arayüz tasarımı ve uygun olduğu görülmüştür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.