Ich würde erwarten
find . -delete
um das aktuelle Verzeichnis zu löschen, aber nicht. Warum nicht?
find . -print
.
cd ..; rm -r dir
mit einer anderen Shell mit ganz klarer Semantik tun ...
Ich würde erwarten
find . -delete
um das aktuelle Verzeichnis zu löschen, aber nicht. Warum nicht?
find . -print
.
cd ..; rm -r dir
mit einer anderen Shell mit ganz klarer Semantik tun ...
Antworten:
Die Mitglieder findutils
wissen , dass es mit * BSD kompatibel ist:
Einer der Gründe, warum wir das Löschen von "." dient der Kompatibilität mit * BSD, aus dem diese Aktion stammt.
Der Quellcode von NEWS in findutils zeigt, dass sie beschlossen haben, das Verhalten beizubehalten:
#20802: If -delete fails, find's exit status will now be non-zero. However, find still skips trying to delete ".".
[AKTUALISIEREN]
Da diese Frage zu einem der aktuellsten Themen wird, tauche ich in den FreeBSD-Quellcode ein und finde einen überzeugenderen Grund.
Sehen wir uns den Quellcode des Dienstprogramms find von FreeBSD an :
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);
}
...
Wie Sie sehen, wird die rmdir()
durch POSIX definierte C-Funktion erreicht, wenn Punkt und Punkt nicht herausgefiltert werden unistd.h
.
Führen Sie einen einfachen Test durch. Rmdir mit dem Argument dot / dot-dot gibt -1 zurück:
printf("%d\n", rmdir(".."));
Schauen wir uns an, wie POSIX rmdir beschreibt :
Wenn sich das path-Argument auf einen Pfad bezieht, dessen letzte Komponente entweder dot oder dot-dot ist, schlägt rmdir () fehl.
Es wurde kein Grund angegeben, warum shall fail
.
Ich fand rename
einige Gründe zu erklären, n:
Das Umbenennen von Punkt oder Punkt-zu-Punkt ist verboten, um zyklische Dateisystempfade zu verhindern.
Zyklische Dateisystempfade ?
Ich schaue mir die C Programming Language (2nd Edition) an und suche nach einem Verzeichnis-Thema. Überraschenderweise habe ich festgestellt, dass der Code ähnlich ist :
if(strcmp(dp->name,".") == 0 || strcmp(dp->name,"..") == 0)
continue;
Und der Kommentar!
Jedes Verzeichnis enthält immer Einträge für sich selbst mit der Bezeichnung "." Und das übergeordnete Verzeichnis "..". Diese müssen übersprungen werden, sonst wird das Programm für immer wiederholt .
"loop forever" , das ist dasselbe wie oben rename
unter "zyklische Dateisystempfade" beschrieben .
Ich habe den Code leicht modifiziert und ihn unter Kali Linux laufen lassen, basierend auf dieser Antwort :
#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);
}
Wir werden sehen:
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$
Es funktioniert korrekt, was nun, wenn ich die continue
Anweisung auskommentiere:
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$
Wie Sie sehen, muss ich Ctrl+ verwenden C, um dieses Endlosschleifenprogramm zu beenden.
Das '..'-Verzeichnis hat seinen ersten Eintrag' .. 'gelesen und wird für immer wiederholt.
Fazit:
GNU findutils
versucht, mit dem find
Dienstprogramm in * BSD kompatibel zu sein .
find
Dienstprogramm in * BSD verwendet intern die rmdir
POSIX-kompatible C-Funktion, die Punkt / Punkt-Punkt nicht zulässt.
Der Grund rmdir
dafür, dass Punkt / Punkt-Punkt nicht zugelassen wird, besteht darin, dass zyklische Dateisystempfade verhindert werden.
Die von K & R geschriebene Programmiersprache C zeigt beispielhaft, wie Punkt / Punkt-Punkt zu einem Endlosschleifenprogramm führt.
Weil Ihr find
Befehl .
als Ergebnis zurückgegeben wird. Von der Infoseite von rm
:
Jeder Versuch, eine Datei zu entfernen, deren letzte Dateinamenskomponente '.' Ist. oder '..' wird gemäß POSIX-Anweisung ohne Aufforderung abgelehnt.
Es sieht also so aus, als würde man sich find
in diesem Fall nur an POSIX-Regeln halten.
/var/log
und Sie es als root ausführen würden und denken, es würde alle Unterverzeichnisse und auch das aktuelle Verzeichnis entfernen?
man
Seite für find
sagt: "Wenn das Entfernen fehlgeschlagen ist, wird eine Fehlermeldung ausgegeben." Warum wird kein Fehler gedruckt?
mkdir foo && cd foo && rmdir $(pwd)
. Es entfernt .
(oder ..
), was nicht funktioniert.
Der Systemaufruf rmdir schlägt mit EINVAL fehl, wenn die letzte Komponente ihres Argumentpfads lautet "."
. Es ist unter http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html dokumentiert. Die Gründe
für das Verhalten sind:
Die Bedeutung des Löschens von Pfadname / Punkt ist unklar, da der Name der Datei (Verzeichnis) im zu entfernenden übergeordneten Verzeichnis nicht eindeutig ist, insbesondere wenn mehrere Links zu einem Verzeichnis vorhanden sind.
Während 林果 林果 und Thomas bereits gute Antworten dazu gaben, habe ich das Gefühl, dass ihre Antworten vergessen haben, zu erklären, warum dieses Verhalten überhaupt implementiert wurde.
In Ihrem find . -delete
Beispiel klingt das Löschen des aktuellen Verzeichnisses ziemlich logisch und vernünftig. Aber bedenken Sie:
$ find . -name marti\*
./martin
./martin.jpg
[..]
Klingt das Löschen für Sie .
immer noch logisch und vernünftig?
Das Löschen eines nicht leeren Verzeichnisses ist ein Fehler - es ist also unwahrscheinlich, dass Sie Daten mit verlieren find
(obwohl Sie dies mit könnten rm -r
) -, aber das aktuelle Arbeitsverzeichnis Ihrer Shell ist auf ein Verzeichnis festgelegt, das nicht mehr existiert, was zu Verwirrung führt und überraschendes Verhalten:
$ pwd
/home/martin/test
$ rm -r ../test
$ touch foo
touch: cannot touch 'foo': No such file or directory
Das Löschen des aktuellen Verzeichnisses ist einfach ein gutes Schnittstellendesign und entspricht dem Prinzip der geringsten Überraschung.