Kenapa tidak ditemukan? -hapus hapus direktori saat ini?


22

Saya harapkan

find . -delete

untuk menghapus direktori saat ini, tetapi tidak. Kenapa tidak?


3
Kemungkinan besar karena menghapus direktori kerja saat ini tidak akan menjadi ide yang baik.
Alexej Magura

Setuju - Saya suka perilaku default, tetapi tidak konsisten dengan, misalnya find . -print,.
mbroshi

@AlexejMagura walaupun saya bersimpati, saya tidak melihat mengapa menghapus direktori saat ini harus berbeda dari menghapus file yang terbuka. Objek akan tetap hidup sampai referensi ada, dan kemudian sampah dikumpulkan sesudahnya. Anda dapat melakukannya cd ..; rm -r dirdengan shell lain dengan semantik yang cukup jelas ...
Rmano

@Rmano ini benar: itu hanya sesuatu yang tidak akan saya lakukan pada prinsipnya: naik saja direktori lalu hapus direktori saat ini. Saya tidak sepenuhnya yakin mengapa ini menjadi masalah besar - meskipun saya memiliki beberapa kemalangan dengan direktori saat ini tidak ada lagi, seperti jalur relatif tidak lagi berfungsi, tetapi Anda selalu dapat keluar dengan menggunakan jalur absolut - tetapi sebagian dari diri saya hanya mengatakan bahwa itu bukan ide yang baik secara umum.
Alexej Magura

Jawaban:


29

Para anggota findutils menyadarinya , ini kompatibel dengan * BSD:

Salah satu alasan mengapa kami melewatkan penghapusan "." untuk kompatibilitas dengan * BSD, tempat tindakan ini berasal.

The NEWS dalam kode findutils sumber menunjukkan bahwa mereka memutuskan untuk menjaga perilaku:

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

[MEMPERBARUI]

Karena pertanyaan ini menjadi salah satu topik hangat, jadi saya menyelami kode sumber FreeBSD dan keluar alasan yang lebih meyakinkan.

Mari kita lihat kode sumber utilitas 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);
    }
...

Seperti yang Anda lihat, jika tidak menyaring titik dan titik-titik, maka itu akan mencapai rmdir()fungsi C yang ditentukan oleh POSIX unistd.h.

Lakukan tes sederhana, rmdir dengan argumen dot / dot-dot akan mengembalikan -1:

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

Mari kita lihat bagaimana POSIX menggambarkan rmdir :

Jika argumen path merujuk ke path yang komponen terakhirnya adalah dot atau dot-dot, rmdir () akan gagal.

Tidak ada alasan yang diberikan mengapa shall fail .

Saya menemukan rename menjelaskan beberapa alasan :

Mengganti nama titik atau titik-titik dilarang untuk mencegah jalur sistem file siklis.

Jalur sistem file siklikal ?

Saya melihat-lihat Bahasa Pemrograman C (Edisi ke-2) dan mencari topik direktori, anehnya saya menemukan kodenya mirip :

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

Dan komentarnya!

Setiap direktori selalu berisi entri untuk dirinya sendiri, yang disebut ".", Dan induknya, ".."; ini harus dilewati, atau program akan berulang selamanya .

"loop forever" , ini sama seperti renamemenggambarkannya sebagai "jalur sistem file siklis" di atas.

Saya sedikit memodifikasi kode dan membuatnya berjalan di Kali Linux berdasarkan jawaban ini :

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

Ayo lihat:

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$ 

Ini berfungsi dengan benar, sekarang bagaimana jika saya berkomentar continueinstruksi:

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$

Seperti yang Anda lihat, saya harus menggunakan Ctrl+ Cuntuk mematikan program loop tak terhingga ini.

Direktori '..' membaca entri pertamanya '..' dan loop selamanya.

Kesimpulan:

  1. GNU findutilsmencoba kompatibel dengan findutilitas di * BSD .

  2. findutilitas dalam * BSD secara internal menggunakan rmdirfungsi C yang kompatibel dengan POSIX yang tidak mengizinkan dot / dot-dot.

  3. Alasan rmdirtidak mengizinkan dot / dot-dot adalah mencegah jalur sistem file siklis.

  4. Bahasa Pemrograman C yang ditulis oleh K&R menunjukkan contoh bagaimana dot / dot-dot akan mengarah ke program loop selamanya.


16

Karena findperintah Anda kembali .sebagai hasilnya. Dari halaman info rm:

Upaya apa pun untuk menghapus file yang komponen nama file terakhirnya adalah '.' atau '..' ditolak tanpa diminta, seperti yang diamanatkan oleh POSIX.

Jadi, sepertinya findhanya menempel pada aturan POSIX dalam kasus ini.


2
Seharusnya: POSIX adalah raja, ditambah menghapus direktori saat ini dapat menyebabkan beberapa masalah yang sangat besar tergantung pada aplikasi induk dan apa yang tidak. Seperti bagaimana jika direktori saat ini berada /var/logdan Anda menjalankannya sebagai root, berpikir bahwa itu akan menghapus semua subdir dan juga menghapus direktori saat ini?
Alexej Magura

1
Itu teori yang bagus, tetapi manhalaman untuk findmengatakan: "Jika penghapusan gagal, pesan kesalahan dikeluarkan." Mengapa tidak ada kesalahan yang dicetak?
mbroshi

1
@AlexejMagura Menghapus direktori saat ini bekerja dengan baik pada umumnya: mkdir foo && cd foo && rmdir $(pwd). Menghapus .(atau ..) itu tidak berhasil.
Tavian Barnes

4

Panggilan sistem rmdir gagal dengan EINVAL jika komponen terakhir dari jalur argumennya adalah ".". Ini didokumentasikan di http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html dan alasan perilaku tersebut adalah:

Arti menghapus pathname / dot tidak jelas, karena nama file (direktori) di direktori induk yang akan dihapus tidak jelas, terutama di hadapan beberapa tautan ke direktori.


2

Panggilan rmdir(".") sebagai panggilan sistem tidak berfungsi ketika saya mencobanya, jadi tidak ada alat level yang lebih tinggi yang dapat berhasil.

Anda harus menghapus direktori melalui nama aslinya bukan .alias.


1

Sementara 林果 皞 dan Thomas sudah memberikan jawaban yang bagus tentang ini, saya merasa bahwa jawaban mereka lupa menjelaskan alasannya perilaku ini diterapkan di tempat pertama.

Dalam find . -deletecontoh Anda menghapus direktori saat ini terdengar sangat logis dan waras. Tapi pertimbangkan:

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

Apakah menghapus . masih terdengar logis dan waras bagi Anda?

Menghapus direktori yang tidak kosong adalah kesalahan - jadi Anda tidak mungkin kehilangan data dengan ini find(walaupun Anda bisa melakukannya rm -r) - tetapi shell Anda akan memiliki direktori kerja saat ini ditetapkan ke direktori yang tidak ada lagi, yang menyebabkan beberapa membingungkan dan perilaku mengejutkan:

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

Tidak menghapus direktori saat ini hanya desain antarmuka yang baik dan sesuai dengan prinsip paling tidak mengejutkan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.