Saya harapkan
find . -delete
untuk menghapus direktori saat ini, tetapi tidak. Kenapa tidak?
find . -print
,.
cd ..; rm -r dir
dengan shell lain dengan semantik yang cukup jelas ...
Saya harapkan
find . -delete
untuk menghapus direktori saat ini, tetapi tidak. Kenapa tidak?
find . -print
,.
cd ..; rm -r dir
dengan shell lain dengan semantik yang cukup jelas ...
Jawaban:
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 rename
menggambarkannya 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 continue
instruksi:
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:
GNU findutils
mencoba kompatibel dengan find
utilitas di * BSD .
find
utilitas dalam * BSD secara internal menggunakan rmdir
fungsi C yang kompatibel dengan POSIX yang tidak mengizinkan dot / dot-dot.
Alasan rmdir
tidak mengizinkan dot / dot-dot adalah mencegah jalur sistem file siklis.
Bahasa Pemrograman C yang ditulis oleh K&R menunjukkan contoh bagaimana dot / dot-dot akan mengarah ke program loop selamanya.
Karena find
perintah 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 find
hanya menempel pada aturan POSIX dalam kasus ini.
/var/log
dan Anda menjalankannya sebagai root, berpikir bahwa itu akan menghapus semua subdir dan juga menghapus direktori saat ini?
man
halaman untuk find
mengatakan: "Jika penghapusan gagal, pesan kesalahan dikeluarkan." Mengapa tidak ada kesalahan yang dicetak?
mkdir foo && cd foo && rmdir $(pwd)
. Menghapus .
(atau ..
) itu tidak berhasil.
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.
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 . -delete
contoh 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.