
Ich wollte nur wissen, warum bei meiner Verwendung rm -rf my-symlink
nur der symbolische Link gelöscht wird, rm -rf my-symlink/
die Dateien im verknüpften Verzeichnis jedoch gelöscht werden und der symbolische Link unverändert bleibt?
Antwort1
Sie können den Unterschied erkennen, indem Sie die Ausgabe von stat my-symlink
und vergleichen stat my-symlink/
. my-symlink
, ohne Schrägstrich, ist der symbolische Link selbst; my-symlink/
, mit Schrägstrich, ist das Verzeichnis, auf das der symbolische Link zeigt. Dies können Sie separat überprüfen, indem Sie die Inodes von und des Verzeichnisses vergleichen, my-symlink/
auf das er zeigt.
Mit diesen Informationen in der Hand stimmt das Verhalten, das Sie sehen, mit dem inrm
's Spezifikation: Löscht bei der Verarbeitung eines symbolischen Links rm
den Link, ohne „hineinzusteigen“, wenn er auf ein Verzeichnis zeigt; bei der Verarbeitung eines Verzeichnisses (mit der -r
Option) löscht es dessen Inhalt rekursiv. In diesem my-symlink/
Fall rm
versucht es zwar, das „Verzeichnis“ zu löschen, schlägt aber fehl, da es sich nicht um ein Verzeichnis, sondern um einen symbolischen Link handelt – aufgrund des -f
Flags verursacht dies jedoch keinen Fehler.
Antwort2
Ich dachte, ich würde das Verhalten etwas genauer untersuchen, also hier noch eine Antwort.
Intern rm
verwendetFTSzum rekursiven Zugriff auf Dateihierarchien. fts_open
nimmt ein Array von Pfaden als Parameter und erstellt für jeden Pfad eine Baumstruktur. Dies ermöglicht dem Programmierer, mehrere Orte nahtlos zu erkunden, als wären sie Teil einer einheitlichen Hierarchie.
Hier ist ein Testprogramm, mit dem Sie selbst mit FTS spielen können.
#include <stdio.h>
#include <stdlib.h>
#include <fts.h>
int main(int argc, char* argv[])
{
if(argc < 2) return EXIT_FAILURE;
char* const* arr = argv + 1;
FTS* hier = fts_open(arr, FTS_NOSTAT | FTS_PHYSICAL, NULL);
FTSENT* ent;
while((ent = fts_read(hier))) {
printf("%s info=%d (D=%d DP=%d F=%d SL=%d)\n",
ent->fts_accpath, ent->fts_info,
ent->fts_info == FTS_D, ent->fts_info == FTS_DP,
ent->fts_info == FTS_F || ent->fts_info == FTS_NSOK,
ent->fts_info == FTS_SL);
}
fts_close(hier);
return EXIT_SUCCESS;
}
Nehmen wir an, wir haben die folgende Verzeichnisstruktur erstellt:
$ mkdir dir
$ touch dir/file
$ ln -s dir sym
Betrachten wir nun Ihren ersten Fall und sehen wir, wie FTS die Untersuchung durchführt.
$ gcc fts.c
$ ./a.out sym
sym info=12 (D=0 DP=0 F=0 SL=1)
Wie Sie sehen, sym
wird in diesem Fall als Datei angesehen. Genauer gesagt als symbolischer Link. Mit diesen Informationen rm
würde es daher als Datei behandelt und aufgerufen unlinkat(AT_FDCWD, "sym", 0)
. Der letzte Parameter (0) bewirkt, unlinkat
dass es sich wie verhält unlink
. Mit anderen Worten: Es löscht einfach eine Datei. Infolgedessen verschwindet Ihr Link.
Sehen wir uns nun an, was mit passiert sym/
.
$ ./a.out sym/
sym/ info=1 (D=1 DP=0 F=0 SL=0)
file info=11 (D=0 DP=0 F=1 SL=0)
sym/ info=6 (D=0 DP=1 F=0 SL=0)
In diesem Fall sym
wurde als Zielverzeichnis behandelt. Wir iterieren zuerst zu sym
und dann sym/file
noch sym
einmal. Letzteres liegt an der Funktionsweise von FTS: Zuerst iteriert es über den Inhalt und kehrt dann zum Stammknoten zurück. Das ist eigentlich ziemlich praktisch für rm
. Im ersten Durchgang ( D
) kann es Dateien löschen und im zweiten ( DP
) die leeren Verzeichnisse entfernen.
Wie Sie sehen, meldet sich FTS sym/
in beiden Fällen als Verzeichnis. Das liegt daran, dass wir den Pfad mit einem abschließenden Schrägstrich angegeben haben, wodurch der Kernel gezwungen wird, ihn als Verzeichnis zu interpretieren. Im Fall eines Links bedeutet dies, dass er diesem auf jeden Fall folgt.In technischeren Begriffen heißt es in den Spezifikationen:
Ein Pfadname, der mindestens ein Zeichen außer einem Schrägstrich enthält und mit einem oder mehreren abschließenden Schrägstrichen endet, wird so aufgelöst, als ob an den Pfadnamen ein einzelner Punkt ( '.' ) angehängt wäre.
Da FTS sym/
als Verzeichnis gemeldet wird, rm
verhält es sich, als würde es ein leeres Verzeichnis löschen. Dementsprechend ruft es auf unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
. Dies führt dazu, unlinkat
dass es sich wie verhält rmdir
.
sym/
Beim Auflösen des Pfads erkennt der Systemaufruf jedoch, unlinkat
dass ihm tatsächlich kein Verzeichnis zugewiesen wird. Er wird daher eine Meldung ausgeben ENOTDIR
, die Folgendes auslöst:
$ rm: cannot remove ‘sym/’: Not a directory
Und tatsächlich, wenn Sie die Flagge aus Ihren Anrufen entfernen -f
... ist das genau das, was Sie sehen werden. Ob dies nun ein Fehler oder ein Feature ist ... ich habe keine Ahnung.