Cela dépend des outils que vous utilisez :vérifions quelques cas :
Si vous exécutez quelque chose dans le sens de mv /path/to/source/* /path/to/dest/
Dans un shell, vous vous retrouverez avec les 1000 fichiers originaux déplacés, les 300 nouveaux étant intacts. Cela vient du fait que le shell étendra le *
avant de commencer l'opération de déplacement, donc lorsque le déplacement est en cours, la liste est déjà fixée.
Si vous utilisez Nautilus (et d'autres amis de l'interface graphique), vous vous retrouverez de la même manière :il exécutera l'opération de déplacement en fonction des fichiers sélectionnés - cela ne change pas lorsque de nouveaux fichiers apparaissent.
Si vous utilisez votre propre programme en utilisant des appels système le long de la ligne de boucle sur glob
et seulement un mv
jusqu'au glob
reste vide, vous vous retrouverez avec les 1300 fichiers dans le nouveau répertoire. C'est parce que chaque nouveau glob
récupérera les nouveaux fichiers qui sont apparus entre-temps.
Lorsque vous dites au système de déplacer tous les fichiers d'un répertoire, il répertorie tous les fichiers, puis commence à les déplacer. Si de nouveaux fichiers apparaissent dans le répertoire, ils ne sont pas ajoutés à la liste des fichiers à déplacer, ils resteront donc à l'emplacement d'origine.
Vous pouvez, bien sûr, programmer une manière de déplacer les fichiers différente de mv
qui vérifiera périodiquement les nouveaux fichiers dans le répertoire source.
Le noyau lui-même ne peut pas être "au milieu" d'une opération "déplacer 1000 fichiers". Vous devez être beaucoup plus précis sur l'opération que vous proposez.
Un thread ne peut déplacer qu'un seul fichier à la fois avec le rename(*oldpath, const char *newpath)
ou renameat
appels système (et uniquement dans le même système de fichiers). Ou Linux renameat2
qui a des drapeaux comme RENAME_EXCHANGE
pour échanger atomiquement deux chemins, ou RENAME_NOREPLACE
à pas remplacer la destination si elle existe. (par exemple, autoriser un mv -i
implémentation qui évite la condition de concurrence de stat
puis rename
, qui écraserait toujours un fichier créé après stat
.link
+ unlink
pourrait également résoudre ce problème, car link
échoue si le nouveau nom existe.)
Mais chacun de ces appels système ne renomme qu'une seule entrée de répertoire par appel système . Utilisation de POSIX renameat
avec olddirfd
et newdirfd
(ouvert avec open(O_DIRECTORY)
) vous permettrait de continuer à parcourir les fichiers d'un répertoire même si le répertoire source ou de destination lui-même avait été renommé. (L'utilisation de chemins relatifs pourrait également permettre cela avec le rename()
normal .)
Quoi qu'il en soit, comme le disent les autres réponses, la plupart des programmes qui utilisent l'appel système rename trouveront une liste de noms de fichiers avant de faire le premier rename
. (Généralement en utilisant le readdir(3)
La bibliothèque POSIX fonctionne comme un wrapper pour les appels système spécifiques à la plate-forme comme Linux getdents
).
Mais si vous parlez de find -exec ... {} \;
pour exécuter une commande par fichier, ou le plus efficace -exec {} +
avec tant de fichiers qu'ils ne tiennent pas sur une seule ligne de commande, vous pouvez certainement renommer tout en scannant. ex.
find . -name '*.txt' -exec mv -t ../txtfiles {} \; # Intentionally inefficient
Si vous avez créé un nouveau .txt
fichiers pendant que cela fonctionnait, vous pourriez voir certains d'entre eux en ../txtfiles
. Mais en interne find(1)
aura utilisé open(O_DIRECTORY)
et getdents
sur .
.
Si un seul appel système suffisait à renvoyer tous les entrées du répertoire en .
(qui find bouclera sur un à la fois, ne faisant d'autres appels système que si nécessaire pour -type
ou pour récurser, ou fork+exec sur une correspondance), alors la liste est un instantané des entrées du répertoire à un moment donné. D'autres modifications apportées au répertoire ne peuvent pas affecter ce que find
fait, car il a déjà une copie du répertoire listant ce qu'il va boucler. (Il utilise probablement en interne readdir(3)
, qui renvoie une entrée à la fois, mais à l'intérieur de la glibc, nous savons en utilisant strace find .
qu'il fait un getdents64
appel système avec une taille de tampon de count=32768
entrées.)
Mais si le répertoire est énorme et/ou le noyau ne remplit pas find
's buffer, il devra faire un deuxième appel système getdents après avoir bouclé sur ce qu'il a obtenu la première fois. Donc, il pourrait peut-être voir de nouvelles entrées après avoir fait quelques changements de nom.
Mais voir la discussion dans les commentaires sous d'autres réponses :le noyau a peut-être pris un instantané pour nous, car (je pense) que getdents n'est pas autorisé à renvoyer deux fois le même nom de fichier. Différents systèmes de fichiers utilisent différents mécanismes de tri / indexation pour rendre l'accès à une entrée dans un énorme répertoire plus efficace qu'une recherche linéaire. Ainsi, l'ajout ou la suppression d'un répertoire peut éventuellement avoir d'autres effets sur l'ordre des entrées restantes. Hmm, il est probablement plus probable que les systèmes de fichiers conservent un ordre stable et mettent simplement à jour un index réel (comme le EXT4 dir_index
fonctionnalité), donc la position d'un répertoire FD peut simplement être une entrée de répertoire à partir de laquelle reprendre ? Je ne sais vraiment pas comment le telldir(3)
l'interface de la bibliothèque correspond à lseek
, ou si c'est purement une chose de l'espace utilisateur pour boucler sur le tampon obtenu par l'espace utilisateur. Mais plusieurs getdents
peut être nécessaire pour obtenir toutes les entrées d'un énorme répertoire, donc même si la recherche n'est pas prise en charge, le noyau doit être capable d'enregistrer une position actuelle.
Note de bas de page 1 :
Pour "se déplacer" entre les systèmes de fichiers, c'est à l'espace utilisateur de copier et de dissocier. (par exemple avec open
et soit read+write
, mmap+write
ou sendfile(2)
ou copy_file_range(2)
, les deux derniers évitant totalement de faire rebondir les données du fichier dans l'espace utilisateur.)