Elimina in modo efficiente directory di grandi dimensioni contenenti migliaia di file


162

Abbiamo un problema con una cartella che diventa ingombrante con centinaia di migliaia di piccoli file.

Ci sono così tanti file che eseguendo rm -rfrestituisce un errore e invece quello che dobbiamo fare è qualcosa di simile:

find /path/to/folder -name "filenamestart*" -type f -exec rm -f {} \;

Funziona ma è molto lento e costantemente non riesce a rimanere senza memoria.

C'è un modo migliore per farlo? Idealmente, vorrei rimuovere l'intera directory senza preoccuparsi del contenuto al suo interno.


17
rm -rf *nella cartella probabilmente non riesce a causa di troppi argomenti; ma cosa rm -rf folder/succede se si desidera rimuovere comunque l'intera directory?
sr_

4
Invece di eliminarlo manualmente, suggerisco di avere la cartella su una partizione separata e semplicemente di smontare il formato && & rimontaggio.
bbaja42,

7
Solo per curiosità: quanti file ci vogliono per rompere rm -rf?
jw013,

7
Probabilmente dovresti rinominare la domanda con qualcosa di più preciso, come "Elimina in modo efficiente directory di grandi dimensioni contenenti migliaia di file". Per eliminare una directory e il suo contenuto, la ricorsione è necessaria per definizione. È possibile scollegare manualmente solo l'inode della directory stessa (probabilmente richiede i privilegi di root), smontare il file system ed eseguirlo fsckper recuperare i blocchi del disco inutilizzati, ma tale approccio sembra rischioso e potrebbe non essere più veloce. Inoltre, il controllo del file system potrebbe comportare comunque il passaggio ricorsivo dell'albero del file system.
jw013,

4
Una volta che avevo un ccachealbero di file così grande, e rmimpiegavo così tanto tempo (e rendevo lento l'intero sistema), fu considerevolmente più veloce copiare tutti gli altri file dal filesystem, formattarli e copiarli di nuovo. Da allora do a questi piccoli file system enormi il loro file system dedicato, così puoi mkfsdirettamente invece di rm.
frostschutz,

Risposte:


213

L'uso di rsync è sorprendentemente veloce e semplice.

mkdir empty_dir
rsync -a --delete empty_dir/    yourdirectory/

La risposta di @ sarath ha menzionato un'altra scelta veloce: Perl! I suoi benchmark sono più veloci di rsync -a --delete.

cd yourdirectory
perl -e 'for(<*>){((stat)[9]<(unlink))}'

fonti:

  1. https://stackoverflow.com/questions/1795370/unix-fast-remove-directory-for-cleaning-up-daily-builds
  2. http://www.slashroot.in/which-is-the-fastest-method-to-delete-files-in-linux

4
Grazie molto utile Uso rsync continuamente, non avevo idea che potessi usarlo per cancellare in questo modo. Molto più veloce di rm -rf
John Powell,

22
rsyncpuò essere più veloce del semplice rm, perché garantisce le eliminazioni nell'ordine corretto, quindi è necessaria una ricomputazione meno forte. Visualizza questa risposta serverfault.com/a/328305/105902
Marki555

7
Qualcuno può modificare l'espressione perl per eliminare in modo ricorsivo tutte le directory e i file all'interno di una directory_to_be_deleted ?
Abhinav,

5
Note: aggiungi -Pun'opzione a rsync per qualche altro display, inoltre, fai attenzione alla sintassi, le barre finali sono obbligatorie. Infine, puoi avviare il comando rsync per la prima volta con l' -nopzione prima di avviare una corsa a secco .
Drasill,

1
-aè uguale -rlptgoD, ma solo per l'eliminazione -rdè necessario
Koen.

39

Qualcuno su Twitter ha suggerito di utilizzare -deleteinvece di-exec rm -f{} \;

Ciò ha migliorato l'efficienza del comando, ma utilizza comunque la ricorsione per passare attraverso tutto.


11
Questo non è standard. GNU findha -delete, e findforse altri .
enzotib,

14
-deletedovrebbe essere sempre preferito a -exec rmquando disponibile, per motivi di sicurezza ed efficienza.
jw013,

6
GNU è lo standard di fatto .
Ron John

17

Che dire qualcosa del tipo: find /path/to/folder -name "filenamestart*" -type f -print0 | xargs -0rn 20 rm -f

È possibile limitare il numero di file da eliminare contemporaneamente modificando l'argomento per il parametro -n. Sono inclusi anche i nomi dei file con spazi vuoti.


2
Probabilmente non è necessario il -n 20bit, poiché xargs dovrebbe comunque limitarsi a dimensioni accettabili dell'elenco di argomenti.
Inutile

Si hai ragione. Ecco una nota man xargs: (...) max-chars characters per command line (...). The largest allowed value is system-dependent, and is calculated as the argument length limit for exec. Quindi l' -nopzione è per quei casi in cui xargs non può determinare la dimensione del buffer della CLI o se il comando eseguito ha dei limiti.
digital_infinity

12

Espandendo uno dei commenti, non penso che tu stia facendo quello che pensi di fare.

Innanzitutto ho creato un'enorme quantità di file, per simulare la tua situazione:

$ mkdir foo
$ cd foo/
$ for X in $(seq 1 1000);do touch {1..1000}_$X; done

Quindi ho provato cosa mi aspettavo di fallire e cosa sembra che tu stia facendo nella domanda:

$ rm -r foo/*
bash: /bin/rm: Argument list too long

Ma questo fa di lavoro:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory

6
Questa è l'unica soluzione che ha funzionato: eseguire rm -Rf bigdirectorypiù volte. Avevo una directory con migliaia di milioni di sottodirectory e file. Non riuscivo nemmeno a correre lso findo rsyncin quella directory, perché ha esaurito la memoria. Il comando è stato rm -Rfchiuso molte volte (memoria insufficiente) eliminando solo parte dei miliardi di file. Ma dopo molti tentativi finalmente ha fatto il suo lavoro. Sembra essere l'unica soluzione se il problema è esaurire la memoria.
Erik,

12

Un trucco intelligente:

rsync -a --delete empty/ your_folder/

È ad alta intensità di CPU, ma è davvero molto veloce. Vedi https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html


Non è così veloce, perché legge i contenuti della directory in modo efficiente. Vedi questa risposta per una soluzione 10x più veloce e spiegazione serverfault.com/a/328305/105902
Marki555

2
@ Marki555: nella modifica della domanda viene riportato 60 secondi per rsync -a --deletevs 43 per lsdent. Il rapporto 10x era per time ls -1 | wc -l vs time ./dentls bigfolder >out.txt(che è un confronto parzialmente equo a causa di > filevs wc -l).
Hastur,

Il problema è che NESSUNO dei comandi laggiù effettivamente eseguono l'operazione di attraversamento desiderata per la cancellazione. Il codice che danno? NON FUNZIONA come descritto da Marki555.
Svartalf,

6

Ho avuto l'opportunità di testare -deleterispetto a -exec rm \{\} \;e per me è -deletestata la risposta a questo problema.

Utilizzando -deletei file eliminati in una cartella di 400.000 file almeno 1.000 volte più veloce di rm.

L'articolo "Come eliminare un gran numero di file in Linux" suggerisce che è circa tre volte più veloce, ma nel mio test la differenza è stata molto più drammatica.


3
L'utilizzo find -execesegue il rmcomando per ogni file separatamente, ecco perché è così lento.
Marki555,

5

Informazioni -deletesull'opzione sopra: la sto usando per rimuovere un numero elevato di file (1M + est) in una cartella temporanea che ho creato e che ho inavvertitamente dimenticato di pulire ogni notte. Ho riempito il mio disco / partizione per errore, e nient'altro poteva rimuoverli se non il find .comando. È lento, all'inizio stavo usando:

find . -ls -exec rm {} \;

Ma ci voleva un tempo ESTREMO. È iniziato dopo circa 15 minuti per rimuovere alcuni file, ma la mia ipotesi è che rimuovesse meno di una decina di secondi al secondo dopo che finalmente è stato avviato. Quindi, ho provato il:

find . -delete

invece, e lo sto lasciando correre adesso. Sembra funzionare più velocemente, anche se è estremamente tassativo sulla CPU che l'altro comando non era. Funziona da circa un'ora e penso che stia recuperando spazio sul mio disco rigido e che la partizione "dimagrisca gradualmente", ma sta ancora impiegando molto tempo. Dubito seriamente che funzioni 1.000 volte più veloce dell'altro. Come in tutte le cose, volevo solo sottolineare il compromesso tra spazio e tempo. Se hai la larghezza di banda della CPU da risparmiare (lo facciamo), esegui quest'ultima. La mia CPU è in esecuzione ( uptimerapporti):

10:59:17 up 539 days, 21:21,  3 users,  load average: 22.98, 24.10, 22.87

E ho visto che la media del carico supera le 30,00, il che non va bene per un sistema occupato, ma per il nostro che è normalmente leggermente caricato, va bene per un paio d'ore. Ho controllato la maggior parte delle altre cose sul sistema e sono ancora reattive, quindi per ora stiamo bene.


se hai intenzione di usarlo execquasi sicuramente non vuoi usare -lse do find . -type f -exec rm '{}' ++ è più veloce perché fornirà a rm tutti gli argomenti che può gestire contemporaneamente.
xenoterracide,

Penso che dovresti andare avanti e modificarlo nella sua risposta ... è davvero troppo lungo per un commento. Inoltre, sembra che il tuo filesystem abbia cancellazioni abbastanza costose, curioso di sapere quale sia? È possibile eseguire che find … -deleteattraverso niceo ionice, che possono aiutare. Quindi potrebbe cambiare alcune opzioni di montaggio in impostazioni meno sicure. (E, ovviamente, a seconda di cos'altro c'è sul filesystem, il modo più veloce per cancellare tutto è spesso mkfs.)
derobert

3
La media del carico non è sempre CPU, è solo una misura del numero di processi bloccati nel tempo. I processi possono bloccare su I / O su disco, il che è probabilmente ciò che sta accadendo qui.
Score_Under

Si noti inoltre che la media del carico non tiene conto del numero di CPU logiche. Quindi loadavg 1per macchine single-core è uguale a loadavg 64su sistemi a 64 core, il che significa che ogni CPU è occupata al 100% del tempo.
Marki555,


3

Prendi in considerazione l'utilizzo del volume Btrfs ed elimina semplicemente l'intero volume per tale directory con un numero elevato di file.

In alternativa puoi creare un file di immagine FS, quindi smontare ed eliminare il suo file per rimuovere tutto in una volta molto velocemente.


2

Supponendo di avere GNU parallelinstallato, ho usato questo:

parallel rm -rf dir/{} ::: `ls -f dir/`

ed è stato abbastanza veloce.


1

L'eliminazione delle directory DAVVERO GRANDI richiede un approccio diverso, come ho appreso da questo sito : è necessario utilizzare ionice e garantisce (con -c3) che le eliminazioni verranno eseguite solo quando il sistema ha IO-time per esso. Il carico dei sistemi non aumenterà fino a raggiungere un livello elevato e tutto rimarrà reattivo (anche se il tempo di ricerca della mia CPU è stato piuttosto elevato a circa il 50%).

find <dir> -type f -exec ionice -c3 rm {} \;

5
usare +invece di \;renderebbe questo più veloce in quanto passa più argomenti per rm in una volta, meno biforcazione
xenoterracide

1
Perché no ionice -c3 find <dir> -type f -delete
jtgd

1

Se hai milioni di file e ogni soluzione sopra mette in difficoltà il tuo sistema puoi provare questa ispirazione:

File nice_delete:

#!/bin/bash

MAX_LOAD=3
FILES=("$@")
BATCH=100

while [ ${#FILES[@]} -gt 0 ]; do
    DEL=("${FILES[@]:0:$BATCH}")
    ionice -c3 rm "${DEL[@]}"
    echo -n "#"
    FILES=("${FILES[@]:$BATCH}")
    while [[ $(cat /proc/loadavg | awk '{print int($1)}') -gt $MAX_LOAD ]]; do
        echo -n "."
        sleep 1
    done
done

E ora elimina i file:

find /path/to/folder -type f -exec ./nice_delete {} \+

Trova creerà batch (vedi getconf ARG_MAX) di alcune decine di migliaia di file e li passerà a nice_delete. Ciò creerà lotti ancora più piccoli per consentire la sospensione quando viene rilevato un sovraccarico.


1

Usa rm -rf directoryinvece di rm -rf *.

Inizialmente stavamo facendo rm -rf *qualcosa nella directory per cancellare il contenuto e pensavamo che fosse il più veloce possibile. Ma poi uno dei nostri ingegneri senior ha suggerito di evitare di usare gli asterischi ( *) e di passare invece nella directory padre, come rm -rf directory.

Dopo un intenso dibattito su come ciò non farebbe differenza, abbiamo deciso di confrontarlo, insieme a un terzo metodo di utilizzo find. Ecco i risultati:

time rm -rf *                   2m17.32s
time rm -rf directory           0m15.60s
time find directory -delete     0m16.97s

rm -rf directoryè di circa 9 VOLTE PIÙ VELOCE di rm -rf *!

Inutile dire che abbiamo comprato una birra a quell'ingegnere !

Quindi ora usiamo rm -rf directory; mkdir directoryper eliminare la directory e ricrearla.


0
ls -1 | xargs rm -rf 

dovrebbe funzionare all'interno della cartella principale


1
lsnon funzionerà a causa della quantità di file nella cartella. Questo è il motivo per cui ho dovuto usare find, grazie comunque.
Toby,

4
@Toby: prova ls -f, che disabilita l'ordinamento. L'ordinamento richiede che l'intera directory venga caricata in memoria per essere ordinata. Un utente non ordinato lsdovrebbe essere in grado di trasmettere in streaming il proprio output.
Camh,

1
Non funziona con nomi di file che contengono nuove righe.
Maxschlepzig,

@camh è vero. Ma la rimozione dei file in ordine ordinato è più rapida che non ordinata (a causa del ricalcolo della btree della directory dopo ogni eliminazione). Vedi questa risposta per un esempio serverfault.com/a/328305/105902
Marki555

@maxschlepzig per tali file che è possibile utilizzare find . -print0 | xargs -0 rm, che utilizzerà il carattere NULL come separatore del nome file.
Marki555,

0

Per il suggerimento di Izkata sopra:

Ma questo fa di lavoro:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory

Questo ha funzionato quasi - o avrebbe funzionato - ma ho avuto alcuni problemi con il permesso; i file erano su un server, ma non capisco ancora da dove provenga questo problema di autorizzazione. Ad ogni modo, Terminal ha chiesto conferma su ogni file. La quantità di file era di circa 20.000, quindi questa non era un'opzione. Dopo "-r" ho aggiunto l'opzione "-f", quindi l'intero comando era " rm -r -f foldername / ". Quindi sembrava funzionare bene. Sono alle prime armi con Terminal, ma suppongo che sia andato tutto bene, vero? Grazie!


0

A seconda di quanto è necessario sbarazzarsi di quei file, suggerirei di utilizzare shred.

$ shred -zuv folder

se si desidera eliminare la directory, ma non è possibile rimuoverla e ricrearla, suggerisco di spostarla e ricrearla all'istante.

mv folder folder_del
mkdir folder
rm -rf folder_del

questo è più veloce, che ci crediate o no, poiché solo un inode deve essere cambiato. Ricorda: non puoi davvero parallelizzare questo gusto su un computer multicore. Dipende dall'accesso al disco, che è limitato dal RAID o da quello che hai.


1
shred non funzionerà con molti filesystem moderni.

0

Se vuoi semplicemente sbarazzarti di molti file il prima possibile, ls -f1 /path/to/folder/with/many/files/ | xargs rmpotrebbe funzionare bene, ma meglio non eseguirlo sui sistemi di produzione perché il tuo sistema potrebbe diventare problemi di I / O e le applicazioni potrebbero bloccarsi durante l'operazione di eliminazione.

Questo script funziona bene per molti file e non dovrebbe influire sull'ioload del sistema.

#!/bin/bash

# Path to folder with many files
FOLDER="/path/to/folder/with/many/files"

# Temporary file to store file names
FILE_FILENAMES="/tmp/filenames"

if [ -z "$FOLDER" ]; then
    echo "Prevented you from deleting everything! Correct your FOLDER variable!"
    exit 1
fi

while true; do
    FILES=$(ls -f1 $FOLDER | wc -l)
    if [ "$FILES" -gt 10000 ]; then
        printf "[%s] %s files found. going on with removing\n" "$(date)" "$FILES"
        # Create new list of files
        ls -f1 $FOLDER | head -n 5002 | tail -n 5000 > "$FILE_FILENAMES"

        if [ -s $FILE_FILENAMES ]; then
            while read FILE; do
                rm "$FOLDER/$FILE"
                sleep 0.005
            done < "$FILE_FILENAMES"
        fi
    else
        printf "[%s] script has finished, almost all files have been deleted" "$(date)"
        break
    fi
    sleep 5
done
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.