Questo dipende dagli strumenti che utilizzi:controlliamo alcuni casi:
Se esegui qualcosa sulla falsariga di mv /path/to/source/* /path/to/dest/
in una shell, ti ritroverai con i 1000 file originali spostati, mentre i nuovi 300 non verranno toccati. Ciò deriva dal fatto che la shell espanderà il *
prima di iniziare l'operazione di spostamento, quindi quando lo spostamento è in corso, l'elenco è già fissato.
Se usi Nautilus (e altri amici della GUI), finirai allo stesso modo:eseguirà l'operazione di spostamento in base ai file selezionati - questo non cambia quando vengono visualizzati nuovi file.
Se usi il tuo programma usando chiamate di sistema lungo la linea del ciclo su glob
e solo uno mv
fino a glob
rimane vuoto, ti ritroverai con tutti i 1300 file nella nuova directory. Questo perché ogni nuovo glob
raccoglierà i nuovi file, che sono comparsi nel frattempo.
Quando dici al sistema di spostare tutti i file da una directory, elenca tutti i file e quindi inizia a spostarli. Se nella directory vengono visualizzati nuovi file, non vengono aggiunti all'elenco dei file da spostare, quindi rimarranno nella posizione originale.
Ovviamente puoi programmare un modo di spostare i file diverso da mv
che verificherà periodicamente la presenza di nuovi file nella directory di origine.
Il kernel stesso non può essere "nel mezzo" di un'operazione di "spostamento di 1000 file". Devi essere molto più specifico su quale operazione stai proponendo.
Un thread può spostare solo un file alla volta con il rename(*oldpath, const char *newpath)
o renameat
chiamate di sistema (e solo all'interno dello stesso filesystem). O Linux renameat2
che ha flag come RENAME_EXCHANGE
per scambiare atomicamente due nomi di percorso, o RENAME_NOREPLACE
non sostituire la destinazione se esiste. (ad es. consentendo un mv -i
implementazione che evita la race condition di stat
e poi rename
, che sovrascriverebbe comunque un file creato dopo stat
.link
+ unlink
potrebbe anche risolverlo, perché link
fallisce se il nuovo nome esiste.)
Ma ognuna di queste chiamate di sistema rinomina solo una singola voce di directory per chiamata di sistema . Usando POSIX renameat
con olddirfd
e newdirfd
(aperto con open(O_DIRECTORY)
) ti permetterebbe di continuare a scorrere i file in una directory anche se la directory di origine o di destinazione stessa era stato rinominato. (L'utilizzo di percorsi relativi potrebbe anche consentirlo con il normale rename()
.)
Ad ogni modo, come dicono le altre risposte, la maggior parte dei programmi che usano la chiamata di sistema rinomina troveranno un elenco di nomi di file prima di eseguire il primo rename
. (Di solito usando il readdir(3)
Funzione della libreria POSIX come wrapper per chiamate di sistema specifiche della piattaforma come Linux getdents
).
Ma se stai parlando di find -exec ... {} \;
per eseguire un comando per file, o il più efficiente -exec {} +
con così tanti file che non si adattano a una riga di comando, è possibile che si verifichino rinominazioni durante la scansione. ad esempio
find . -name '*.txt' -exec mv -t ../txtfiles {} \; # Intentionally inefficient
Se hai creato un nuovo file .txt
mentre era in esecuzione, potresti vederne alcuni in ../txtfiles
. Ma internamente find(1)
avrà usato open(O_DIRECTORY)
e getdents
su .
.
Se una chiamata di sistema fosse sufficiente per restituire tutto le voci della directory in .
(che find eseguirà il loop uno alla volta, effettuando solo ulteriori chiamate di sistema se necessario per -type
o per ricorrere, o fork+exec su una corrispondenza), quindi l'elenco è un'istantanea delle voci della directory in un determinato momento. Ulteriori modifiche alla directory non possono influenzare ciò che find
fa, perché ha già una copia della directory che elenca ciò su cui eseguirà il ciclo. (Probabilmente utilizza internamente readdir(3)
, che restituisce una voce alla volta, ma all'interno di glibc lo sappiamo dall'uso di strace find .
che fa un getdents64
chiamata di sistema con una dimensione del buffer di count=32768
voci.)
Ma se la directory è enorme e/o il kernel non riempie find
's buffer, dovrà effettuare una seconda chiamata di sistema getdents dopo aver ripetuto in loop ciò che ha ottenuto la prima volta. Quindi potrebbe forse vedere nuove voci dopo aver fatto alcune rinominazioni.
Ma vedi la discussione nei commenti sotto altre risposte:il kernel potrebbe aver fatto un'istantanea per noi, perché (penso) getdents non è autorizzato a restituire lo stesso nome file due volte. Diversi filesystem utilizzano diversi meccanismi di ordinamento / indicizzazione per rendere l'accesso a una voce in una directory enorme più efficiente di una ricerca lineare. Quindi l'aggiunta o la rimozione di una directory potrebbe avere altri effetti sull'ordine delle voci rimanenti. Hmm, probabilmente è più probabile che i filesystem mantengano un ordine stabile e aggiornino semplicemente un indice effettivo (come EXT4 dir_index
caratteristica), quindi la posizione di una directory FD può essere solo una voce di directory da cui riprendere? Davvero non so come il telldir(3)
l'interfaccia della libreria è mappata su lseek
, o se è puramente una cosa dello spazio utente per eseguire il looping sul buffer ottenuto dallo spazio utente. Ma più getdents
può essere necessario per ottenere tutte le voci da una directory enorme, quindi anche se la ricerca non è supportata, il kernel deve essere in grado di registrare una posizione corrente.
Nota 1:
Per "spostarsi" tra i filesystem, spetta allo spazio utente copiare e scollegare. (ad es. con open
e read+write
, mmap+write
o sendfile(2)
o copy_file_range(2)
, gli ultimi due evitano totalmente di far rimbalzare i dati del file attraverso lo spazio utente.)