Tôi mong chờ
find . -delete
để xóa thư mục hiện tại, nhưng nó không. Tại sao không?
find . -print
.
cd ..; rm -r dir
với một shell khác với ngữ nghĩa khá rõ ràng ...
Tôi mong chờ
find . -delete
để xóa thư mục hiện tại, nhưng nó không. Tại sao không?
find . -print
.
cd ..; rm -r dir
với một shell khác với ngữ nghĩa khá rõ ràng ...
Câu trả lời:
Các thành viên findutils
nhận thức được điều này , nó tương thích với * BSD:
Một trong những lý do khiến chúng tôi bỏ qua việc xóa "." là để tương thích với * BSD, nơi hành động này bắt nguồn.
Các NEWS trong mã nguồn findutils cho thấy rằng họ đã quyết định để giữ cho hành vi:
#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".
[CẬP NHẬT]
Vì câu hỏi này trở thành một trong những chủ đề nóng, vì vậy tôi đi sâu vào mã nguồn FreeBSD và đưa ra một lý do thuyết phục hơn.
Hãy xem mã nguồn tiện ích của 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);
}
...
Như bạn có thể thấy, nếu nó không lọc ra dấu chấm và dấu chấm, thì nó sẽ đạt đến rmdir()
chức năng C được xác định bởi POSIX unistd.h
.
Thực hiện một thử nghiệm đơn giản, rmdir với đối số dot / dot-dot sẽ trả về -1:
printf("%d\n", rmdir(".."));
Hãy xem POSIX mô tả rmdir như thế nào :
Nếu đối số đường dẫn đề cập đến một đường dẫn có thành phần cuối cùng là dấu chấm hoặc dấu chấm, thì rmdir () sẽ thất bại.
Không có lý do được đưa ra tại sao shall fail
.
Tôi tìm thấy rename
giải thích một số reaso n:
Đổi tên dấu chấm hoặc dấu chấm chấm bị cấm để ngăn chặn đường dẫn hệ thống tệp theo chu kỳ.
Đường dẫn hệ thống tập tin chu kỳ ?
Tôi xem qua Ngôn ngữ lập trình C (Phiên bản 2) và tìm kiếm chủ đề thư mục, thật ngạc nhiên tôi thấy mã này tương tự :
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
Và bình luận!
Mỗi thư mục luôn chứa các mục nhập cho chính nó, được gọi là "." Và cha mẹ của nó, ".."; những cái này phải được bỏ qua, hoặc chương trình sẽ lặp lại mãi mãi .
"Vòng lặp mãi mãi" , điều này giống như cách rename
mô tả nó như là "đường dẫn hệ thống tệp theo chu kỳ" ở trên.
Tôi sửa đổi một chút mã và để nó chạy trong Kali Linux dựa trên câu trả lời này :
#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);
}
Hãy xem:
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$
Nó hoạt động chính xác, bây giờ nếu tôi nhận xét continue
hướng dẫn:
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$
Như bạn có thể thấy, tôi phải sử dụng Ctrl+ Cđể giết chương trình vòng lặp vô hạn này.
Thư mục '..' đọc mục đầu tiên '..' và lặp lại mãi mãi.
Phần kết luận:
GNU findutils
cố gắng tương thích với find
tiện ích trong * BSD .
find
tiện ích trong * BSD trong nội bộ sử dụng rmdir
chức năng C tương thích POSIX mà không cho phép chấm / chấm-chấm.
Lý do rmdir
không cho phép dot / dot-dot là ngăn đường dẫn hệ thống tệp theo chu kỳ.
Ngôn ngữ lập trình C được viết bởi K & R cho thấy ví dụ về cách dot / dot-dot sẽ dẫn đến chương trình vòng lặp mãi mãi.
Bởi vì find
lệnh của bạn trả về .
kết quả. Từ trang thông tin của rm
:
Mọi nỗ lực xóa tệp có thành phần tên tệp cuối cùng là '.' hoặc '..' bị từ chối mà không có bất kỳ lời nhắc nào, như được ủy quyền bởi POSIX.
Vì vậy, có vẻ như find
chỉ cần tuân thủ các quy tắc POSIX trong trường hợp này.
/var/log
và bạn đã chạy nó với quyền root, nghĩ rằng nó sẽ xóa tất cả các thư mục con và nó cũng xóa thư mục hiện tại?
man
trang cho find
biết: "Nếu xóa không thành công, một thông báo lỗi được đưa ra." Tại sao không có lỗi in?
mkdir foo && cd foo && rmdir $(pwd)
. Đó là loại bỏ .
(hoặc ..
) không hoạt động.
Cuộc gọi hệ thống rmdir không thành công với EINVAL nếu thành phần cuối cùng của đường dẫn đối số của nó là "."
. Nó được ghi lại tại http://pub.opengroup.org/onlinepub/009695399/fifts/rmdir.html
và lý do căn bản cho hành vi là:
Ý nghĩa của việc xóa tên đường dẫn / dấu chấm là không rõ ràng, vì tên của tệp (thư mục) trong thư mục mẹ cần xóa không rõ ràng, đặc biệt là sự hiện diện của nhiều liên kết đến một thư mục.
Trong khi 林果 皞 và Thomas đã đưa ra câu trả lời tốt về vấn đề này, tôi cảm thấy rằng câu trả lời của họ đã quên giải thích lý do tại sao hành vi này được thực hiện ngay từ đầu.
Trong find . -delete
ví dụ của bạn, việc xóa thư mục hiện tại nghe có vẻ hợp lý và lành mạnh. Nhưng hãy xem xét:
$ find . -name marti\*
./martin
./martin.jpg
[..]
Xóa .
vẫn có vẻ hợp lý và lành mạnh với bạn?
Xóa một thư mục không trống là một lỗi - vì vậy bạn không thể mất dữ liệu với điều này find
(mặc dù bạn có thể với rm -r
) - nhưng shell của bạn sẽ có thư mục làm việc hiện tại được đặt thành một thư mục không còn tồn tại, dẫn đến một số nhầm lẫn và hành vi đáng ngạc nhiên:
$ pwd
/home/martin/test
$ rm -r ../test
$ touch foo
touch: cannot touch 'foo': No such file or directory
Không xóa thư mục hiện tại chỉ đơn giản là thiết kế giao diện tốt và tuân thủ nguyên tắc ít gây bất ngờ nhất.