Я бы ожидал
find . -delete
удалить текущий каталог, но это не так. Почему бы нет?
find . -print
.
cd ..; rm -r dir
с другой оболочкой с довольно четкой семантикой ...
Я бы ожидал
find . -delete
удалить текущий каталог, но это не так. Почему бы нет?
find . -print
.
cd ..; rm -r dir
с другой оболочкой с довольно четкой семантикой ...
Ответы:
Члены об этом findutils
знают , это для совместимости с * BSD:
Одна из причин, по которой мы пропускаем удаление "." для совместимости с * BSD, где это действие возникло.
NEWS в коде показывает Findutils источника , что они решили сохранить поведение:
#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".
[ОБНОВИТЬ]
Поскольку этот вопрос стал одной из горячих тем, я углублюсь в исходный код FreeBSD и укажу более убедительную причину.
Давайте посмотрим на утилиту поиска исходного кода 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);
}
...
Как вы можете видеть, если он не отфильтровывает точку и точку-точку, то он достигнет rmdir()
функции C, определенной в POSIX unistd.h
.
Сделайте простой тест, rmdir с аргументом точка / точка-точка вернет -1:
printf("%d\n", rmdir(".."));
Давайте посмотрим, как POSIX описывает rmdir :
Если аргумент пути ссылается на путь, конечный компонент которого - точка или точка-точка, rmdir () завершится ошибкой.
Не было дано объяснения почему shall fail
.
Я нашел rename
объяснить некоторые причины :
Переименование точки или точки-точки запрещено для предотвращения циклических путей файловой системы.
Циклические пути файловой системы ?
Я просматриваю язык программирования C (2-е издание) и ищу тему каталога, неожиданно обнаружил, что код похож :
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
И комментарий!
Каждый каталог всегда содержит записи для себя, называемые ".", И его родитель, ".."; они должны быть пропущены, иначе программа зациклится навсегда .
«цикл навсегда» , это то же самое, что 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);
}
Посмотрим:
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, показывает пример того, как точка / точка-точка приведет к созданию программы цикла навсегда.
Потому что ваша find
команда возвращается .
как результат. Со страницы информации rm
:
Любая попытка удалить файл с последним компонентом имени файла '.' или «..» отклоняется без каких-либо подсказок, как того требует POSIX.
Таким образом, find
в данном случае , похоже, просто придерживаться правил POSIX.
/var/log
и вы запустили его как root, думая, что он удалит все подкаталоги, а также удалит текущий каталог?
man
странице find
написано: «Если удаление не удалось, выдается сообщение об ошибке». Почему не печатается ошибка?
mkdir foo && cd foo && rmdir $(pwd)
. Это удаление .
(или ..
), которое не работает.
Системный вызов rmdir завершается неудачно с EINVAL, если последний компонент его пути аргумента "."
. Он задокументирован по адресу http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html,
и обоснование такого поведения:
Смысл удаления pathname / dot неясен, поскольку имя файла (каталога) в родительском каталоге, который нужно удалить, неясно, особенно при наличии нескольких ссылок на каталог.
Хотя 皞 皞 и Томас уже дали хорошие ответы на этот вопрос, я чувствую, что их ответы забыли объяснить, почему это поведение было реализовано в первую очередь.
В вашем find . -delete
примере удаление текущего каталога звучит довольно логично и разумно. Но учтите:
$ find . -name marti\*
./martin
./martin.jpg
[..]
.
Звучит ли удаление по- прежнему логично и нормально для вас?
Удаление непустого каталога является ошибкой - поэтому вы вряд ли потеряете данные с этим find
(хотя вы могли бы с этим rm -r
) - но в вашей оболочке текущий рабочий каталог будет установлен в каталог, который больше не существует, что приведет к некоторой путанице и удивительное поведение:
$ pwd
/home/martin/test
$ rm -r ../test
$ touch foo
touch: cannot touch 'foo': No such file or directory
Не удаляя текущий каталог, это просто хороший дизайн интерфейса и соответствует принципу наименьшего удивления.