mbr*_*shi 23 directory find rm
我希望
find . -delete
Run Code Online (Sandbox Code Playgroud)
删除当前目录,但它没有。为什么不?
Fru*_*uit 30
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 ".".
[更新]
由于这个问题成为热门话题之一,所以我深入研究了 FreeBSD 源代码并得出了一个更有说服力的理由。
让我们看看FreeBSD的find 实用程序源代码:
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);
}
...
Run Code Online (Sandbox Code Playgroud)
如您所见,如果它不过滤掉点和点点,那么它将到达rmdir()由 POSIX 定义的 C 函数unistd.h。
做一个简单的测试,带有 dot/dot-dot 参数的 rmdir 将返回 -1:
printf("%d\n", rmdir(".."));
Run Code Online (Sandbox Code Playgroud)
我们来看看POSIX是如何描述 rmdir 的:
如果路径参数指的是最终组件是点或点点的路径,则 rmdir() 将失败。
没有给出原因shall fail。
我发现rename 解释了一些原因:
禁止重命名 dot 或 dot-dot 以防止循环文件系统路径。
循环文件系统路径?
我查看了C 编程语言(第二版)并搜索目录主题,令人惊讶的是我发现代码是相似的:
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
Run Code Online (Sandbox Code Playgroud)
还有评论!
每个目录总是包含其自身的条目,称为“.”,及其父目录,“..”;这些必须被跳过,否则程序将永远循环。
“永远循环”,这与上面如何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);
}
Run Code Online (Sandbox Code Playgroud)
让我们来看看:
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$
Run Code Online (Sandbox Code Playgroud)
它工作正常,现在如果我注释掉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$
Run Code Online (Sandbox Code Playgroud)
如您所见,我必须使用Ctrl+C来终止这个无限循环程序。
'..' 目录读取它的第一个条目 '..' 并永远循环。
结论:
GNUfindutils尝试与*BSD 中的find实用程序兼容。
find*BSD 中的实用程序在内部使用rmdir符合 POSIX 的 C 函数,而点/点-点是不允许的。
rmdir不允许点/点点的原因是防止循环文件系统路径。
K&R 编写的 C 编程语言展示了点/点-点如何导致永远循环程序的示例。
Tho*_*mas 16
因为您的find命令.作为结果返回。从信息页面rm:
任何删除最后一个文件名组成部分为“.”的文件的尝试。或 '..' 在没有任何提示的情况下被拒绝,如 POSIX 所规定的。
因此,find在这种情况下,它看起来只是坚持 POSIX 规则。