Mi aspetterei
find . -delete
per eliminare la directory corrente, ma non è così. Perchè no?
find . -print
.
cd ..; rm -r dir
con un'altra shell con semantica abbastanza chiara ...
Mi aspetterei
find . -delete
per eliminare la directory corrente, ma non è così. Perchè no?
find . -print
.
cd ..; rm -r dir
con un'altra shell con semantica abbastanza chiara ...
Risposte:
I membri ne sono a findutils
conoscenza , è compatibile con * BSD:
Uno dei motivi per cui saltiamo la cancellazione di "." è per compatibilità con * BSD, dove ha avuto origine questa azione.
Le NEWS nel codice sorgente di findutils mostrano che hanno deciso di mantenere il comportamento:
#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".
[AGGIORNARE]
Dal momento che questa domanda diventa uno dei temi caldi, quindi mi tuffo nel codice sorgente di FreeBSD e ne esco un motivo più convincente.
Vediamo il codice sorgente dell'utilità di ricerca di 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);
}
...
Come puoi vedere, se non filtra punto e punto-punto, raggiungerà la rmdir()
funzione C definita da POSIX unistd.h
.
Fai un semplice test, rmdir con argomento dot / dot-dot restituirà -1:
printf("%d\n", rmdir(".."));
Diamo un'occhiata a come POSIX descrive rmdir :
Se l'argomento path si riferisce a un percorso il cui componente finale è punto o punto-punto, rmdir () fallirà.
Nessun motivo è stato dato il perché shall fail
.
Ho trovato rename
spiegare alcuni riassunti :
La ridenominazione di punti o punti è vietata per prevenire percorsi ciclici del file system.
Percorsi ciclici del file system ?
Guardo The C Programming Language (2nd Edition) e cerco l' argomento della directory, sorprendentemente ho scoperto che il codice è simile :
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
E il commento!
Ogni directory contiene sempre voci per sé, chiamate ".", E il suo genitore, ".."; questi devono essere saltati, altrimenti il programma si ripeterà per sempre .
"loop per sempre" , è lo stesso di come rename
descriverlo come "percorsi ciclici del file system" sopra.
Modifico leggermente il codice e per farlo funzionare in Kali Linux in base a questa risposta :
#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);
}
Vediamo:
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$
Funziona correttamente, ora cosa succede se commento le continue
istruzioni:
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$
Come puoi vedere, devo usare Ctrl+ Cper uccidere questo programma a ciclo infinito.
La directory '..' legge la sua prima voce '..' e continua per sempre.
Conclusione:
GNU findutils
prova a essere compatibile con l' find
utilità in * BSD .
find
l'utilità in * BSD utilizza internamente la rmdir
funzione C conforme a POSIX che punto / punto-punto non è consentita.
Il motivo per rmdir
non consentire punto / punto-punto è prevenire percorsi ciclici del file system.
Il linguaggio di programmazione C scritto da K&R mostra l'esempio di come punto / punto-punto porterà al programma per sempre.
Perché il tuo find
comando ritorna .
come risultato. Dalla pagina di informazioni di rm
:
Qualsiasi tentativo di rimuovere un file il cui ultimo componente del nome file è "." o '..' viene rifiutato senza alcuna richiesta, come richiesto da POSIX.
Quindi, sembra find
solo attenersi alle regole POSIX in questo caso.
/var/log
e la eseguissi come root, pensando che rimuoverà tutti i sottodir e rimuoverà anche la directory corrente?
man
pagina per find
dice: "Se la rimozione non è riuscita, viene emesso un messaggio di errore". Perché non viene stampato alcun errore?
mkdir foo && cd foo && rmdir $(pwd)
. È rimuovere .
(o ..
) che non funziona.
La chiamata di sistema rmdir non riesce con EINVAL se è l'ultimo componente del percorso dell'argomento "."
. È documentato su http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html
e la logica del comportamento è:
Il significato dell'eliminazione di nome percorso / punto non è chiaro, poiché il nome del file (directory) nella directory principale da rimuovere non è chiaro, in particolare in presenza di più collegamenti a una directory.
Mentre 林果 皞 e Thomas hanno già dato buone risposte su questo, sento che le loro risposte hanno dimenticato di spiegare perché questo comportamento è stato implementato in primo luogo.
Nel tuo find . -delete
esempio, l'eliminazione della directory corrente suona piuttosto logica e sana. Ma considera:
$ find . -name marti\*
./martin
./martin.jpg
[..]
La cancellazione ti sembra .
ancora logica e sana?
L'eliminazione di una directory non vuota è un errore - quindi è improbabile che tu perda dati con questo find
(anche se potresti farlo con rm -r
) - ma la tua shell avrà la sua directory di lavoro corrente impostata su una directory che non esiste più, portando a un po 'di confusione e comportamento sorprendente:
$ pwd
/home/martin/test
$ rm -r ../test
$ touch foo
touch: cannot touch 'foo': No such file or directory
Non eliminare la directory corrente è semplicemente una buona progettazione dell'interfaccia e rispetta il principio della minima sorpresa.