Dies hängt davon ab, welche Tools Sie verwenden:Lassen Sie uns einige Fälle überprüfen:
Wenn Sie etwas in der Art von mv /path/to/source/* /path/to/dest/
ausführen Int einer Shell werden am Ende die ursprünglichen 1000 Dateien verschoben, die neuen 300 bleiben unberührt. Dies kommt daher, dass die Shell die *
erweitert vor Beginn des Verschiebevorgangs, also wenn der Verschiebevorgang läuft, ist die Liste bereits fixiert.
Wenn Sie Nautilus (und andere GUI-Freunde) verwenden, werden Sie auf die gleiche Weise enden:Es wird die Verschiebeoperation basierend darauf ausführen, welche Dateien ausgewählt wurden - dies ändert sich nicht, wenn neue Dateien angezeigt werden.
Wenn Sie Ihr eigenes Programm verwenden, verwenden Sie Systemaufrufe entlang der Schleifenlinie über glob
und nur eine mv
bis glob
leer bleibt, landen am Ende alle 1300 Dateien im neuen Verzeichnis. Denn jeder neue glob
holt die neuen Dateien ab, die in der Zwischenzeit aufgetaucht sind.
Wenn Sie das System anweisen, alle Dateien aus einem Verzeichnis zu verschieben, listet es alle Dateien auf und beginnt dann mit dem Verschieben. Wenn neue Dateien im Verzeichnis erscheinen, werden sie nicht zur Liste der zu verschiebenden Dateien hinzugefügt, sodass sie am ursprünglichen Speicherort verbleiben.
Sie können natürlich eine andere Methode zum Verschieben von Dateien als mv
programmieren die regelmäßig nach neuen Dateien im Quellverzeichnis sucht.
Der Kernel selbst kann nicht "in der Mitte" einer Operation "1000 Dateien verschieben" sein. Sie müssen viel genauer angeben, welche Operation Sie vorschlagen.
Ein Thread kann mit dem rename(*oldpath, const char *newpath)
nur jeweils eine Datei verschieben oder renameat
Systemaufrufe (und nur innerhalb desselben Dateisystems). Oder Linux renameat2
die Flags wie RENAME_EXCHANGE
hat um zwei Pfadnamen atomar auszutauschen, oder RENAME_NOREPLACE
zu nicht Ersetzen Sie das Ziel, falls vorhanden. (z.B. Erlauben eines mv -i
Implementierung, die die Racebedingung von stat
vermeidet und dann rename
, was immer noch eine nach stat
erstellte Datei überschreiben würde .link
+ unlink
könnte das auch lösen, denn link
schlägt fehl, wenn der neue Name existiert.)
Aber jeder dieser Systemaufrufe benennt nur einen einzigen Verzeichniseintrag pro Systemaufruf um . Mit POSIX renameat
mit olddirfd
und newdirfd
(geöffnet mit open(O_DIRECTORY)
) würde es Ihnen ermöglichen, Dateien in einem Verzeichnis zu durchlaufen, selbst wenn das Quell- oder Zielverzeichnis selbst umbenannt worden war. (Die Verwendung relativer Pfade könnte dies auch mit regulärem rename()
ermöglichen .)
Wie auch immer, wie die anderen Antworten sagen, werden die meisten Programme, die den Rename-Systemaufruf verwenden, eine Liste von Dateinamen ermitteln, bevor sie den ersten rename
ausführen . (Normalerweise mit readdir(3)
Die POSIX-Bibliothek fungiert als Wrapper für plattformspezifische Systemaufrufe wie Linux getdents
).
Aber wenn Sie von find -exec ... {} \;
sprechen um einen Befehl pro Datei auszuführen, oder das effizientere -exec {} +
Bei so vielen Dateien, dass sie nicht auf eine Befehlszeile passen, können Sie sicherlich Umbenennungen vornehmen, während Sie noch scannen. z. B.
find . -name '*.txt' -exec mv -t ../txtfiles {} \; # Intentionally inefficient
Wenn Sie einen neuen .txt
erstellt haben Dateien, während dies ausgeführt wurde, möglicherweise siehe einige davon in ../txtfiles
. Aber intern find(1)
wird open(O_DIRECTORY)
verwendet haben und getdents
auf .
.
Wenn ein Systemaufruf ausreichen würde, um alle zurückzugeben die Verzeichniseinträge in .
(wobei find nacheinander eine Schleife durchläuft und nur dann weitere Systemaufrufe durchführt, wenn dies für -type
erforderlich ist oder rekursiv oder fork+exec bei einer Übereinstimmung), dann ist die Liste eine Momentaufnahme der Verzeichniseinträge zu einem bestimmten Zeitpunkt. Weitere Änderungen am Verzeichnis können sich nicht auf find
auswirken tut, weil es bereits eine Kopie des Verzeichnisses hat, das auflistet, was es durchlaufen wird. (Wahrscheinlich verwendet es intern readdir(3)
, das einen Eintrag nach dem anderen zurückgibt, aber innerhalb von glibc kennen wir es von der Verwendung von strace find .
dass es einen getdents64
macht Systemaufruf mit einer Puffergröße von count=32768
Einträge.)
Aber wenn das Verzeichnis riesig ist und/oder der Kernel find
nicht füllt 's-Puffer, muss es einen zweiten getdents-Systemaufruf machen, nachdem es das, was es beim ersten Mal bekommen hat, durchlaufen hat. So könnte es vielleicht neue Einträge sehen, nachdem es einige Umbenennungen vorgenommen hat.
Aber siehe Diskussion in den Kommentaren unter anderen Antworten:Der Kernel hat möglicherweise einen Snapshot für uns erstellt, da getdents (glaube ich) nicht zweimal denselben Dateinamen zurückgeben darf. Unterschiedliche Dateisysteme verwenden unterschiedliche Sortier-/Indizierungsmechanismen, um den Zugriff auf einen Eintrag in einem riesigen Verzeichnis effizienter zu gestalten als eine lineare Suche. Das Hinzufügen oder Entfernen eines Verzeichnisses kann also möglicherweise andere Auswirkungen auf die Reihenfolge der verbleibenden Einträge haben. Hmm, wahrscheinlich ist es wahrscheinlicher, dass Dateisysteme eine stabile Ordnung beibehalten und nur einen aktuellen Index aktualisieren (wie den EXT4 dir_index
Feature), so dass die Position eines Verzeichnis-FDs nur ein Verzeichniseintrag sein kann, von dem aus fortgefahren werden kann? Ich weiß wirklich nicht, wie der telldir(3)
Bibliotheksschnittstelle wird auf lseek
abgebildet , oder wenn dies nur eine Sache des Benutzerraums ist, um den vom Benutzerraum erhaltenen Puffer zu durchlaufen. Aber mehrfach getdents
kann erforderlich sein, um alle Einträge aus einem riesigen Verzeichnis zu erhalten, also muss der Kernel in der Lage sein, eine aktuelle Position aufzuzeichnen, selbst wenn das Suchen nicht unterstützt wird.
Fußnote 1:
Um sich zwischen Dateisystemen zu "bewegen", muss der Benutzerbereich kopieren und die Verknüpfung aufheben. (z.B. mit open
und entweder read+write
, mmap+write
oder sendfile(2)
oder copy_file_range(2)
, die beiden letzteren vermeiden es vollständig, die Dateidaten durch den Benutzerbereich zu senden.)