Члены findutils
знают об этом, это для совместимости с *BSD:
Одна из причин, по которой мы пропускаем удаление "." - это совместимость с *BSD, где это действие возникло.
NEWS в исходном коде findutils показывает, что они решили сохранить такое поведение:
#20802: Если -delete не удается, статус выхода find теперь будет ненулевым. Однако find по-прежнему пропускает попытку удалить ".".
[UPDATE]
Поскольку этот вопрос стал одной из горячих тем, я заглянул в исходный код FreeBSD и нашел более убедительную причину.
Давайте посмотрим на исходный код утилиты find во 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);
}
...
Как вы видите, если она не отфильтрует точки и dot-dot, то дойдет до rmdir()
C-функции, определенной в unistd.h
POSIX.
Проведите простой тест, rmdir с аргументом dot/dot-dot вернет -1:
printf("%d\n", rmdir(".."));
Давайте посмотрим, как POSIX описывает rmdir:
Если аргумент path ссылается на путь, конечным компонентом которого является либо dot, либо dot-dot. dot или dot-dot, rmdir() завершится неудачей.
Причина отказа не была указана
.
Я нашел rename
объяснение некоторых причинn:
Переименование точек или dot-dot запрещено для предотвращения циклических пути файловой системы.
Циклические пути файловой системы ?
Я просматриваю The C Programming Language (2nd Edition) и ищу тему каталога, на удивление я нашел код похожий:
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
И комментарий !
Каждый каталог всегда содержит записи для себя, называемые ".", и своего родителя, "."; их нужно пропускать, иначе программа будет циклиться вечно.
"вечный цикл", это похоже на то, как rename
описывает "циклические пути файловой системы" выше.
Я немного изменил код и заставил его работать в Kali Linux на основе этого ответа:
#include
#include
#include
#include
#include
#include
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);
}
Давайте посмотрим:
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$
Он работает правильно, теперь что если я закомментирую инструкцию 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$
Как вы можете видеть, я должен использовать Ctrl+C, чтобы убить эту программу бесконечного цикла.
Директория '.' считывает свою первую запись '.' и зацикливается до бесконечности.
Вывод:
GNU findutils
пытается совместить с find
утилитой в *BSD.
find
утилита в *BSD внутренне использует rmdir
POSIX-совместимую C функцию, которая точка/точка не разрешена.
Причина, по которой rmdir
не разрешает точку/точку, заключается в предотвращении циклических путей файловой системы.
Язык программирования C, написанный K&R, показывает пример того, как точка/dot-dot приведет к вечному циклу программы.