Supprimer efficacement les grands répertoires contenant des milliers de fichiers


162

Nous avons un problème avec un dossier qui devient lourd avec des centaines de milliers de fichiers minuscules.

Il y a tellement de fichiers que l'exécution rm -rfrenvoie une erreur et nous devons plutôt procéder de la manière suivante:

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

Cela fonctionne mais est très lent et échoue constamment de manquer de mémoire.

Y a-t-il une meilleure manière de faire cela? Idéalement, je voudrais supprimer tout le répertoire sans me soucier de son contenu.


17
rm -rf *dans le dossier échoue probablement à cause d'un trop grand nombre d'arguments; mais qu'en est-il rm -rf folder/si vous voulez quand même supprimer tout le répertoire?
Sr.

4
Au lieu de le supprimer manuellement, je suggère de placer le dossier sur une partition séparée et de démonter simplement && format && remount.
bbaja42

7
Juste par curiosité - combien de fichiers faut-il pour casser rm -rf?
jw013

7
Vous devriez probablement renommer la question en quelque chose de plus précis, par exemple "Supprimer efficacement le grand répertoire contenant des milliers de fichiers". Pour supprimer un répertoire et son contenu, la récursivité est nécessaire par définition. Vous pouvez dissocier manuellement uniquement l'inode du répertoire lui-même (nécessite probablement des privilèges root), démonter le système de fichiers et l'exécuter fsckpour récupérer les blocs de disque inutilisés, mais cette approche semble risquée et peut-être pas plus rapide. De plus, la vérification du système de fichiers peut impliquer de toute façon une traversée récursive de l’arborescence du système de fichiers.
jw013

4
Une fois que mon ccachearborescence de fichiers était si volumineuse et rmsi longue (et rendant tout le système lent), il était beaucoup plus rapide de copier tous les autres fichiers du système de fichiers, de les formater et de les recopier. Depuis lors, je donne à de tels petits arbres de fichiers leur propre système de fichiers, de sorte que vous pouvez mkfsdirectement au lieu de rm.
Frostschutz

Réponses:


213

Utiliser rsync est surprenant, rapide et simple.

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

La réponse de @ sarath mentionnait un autre choix rapide: Perl! Ses repères sont plus rapides que rsync -a --delete.

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

Sources:

  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
Merci, très utile. J'utilise rsync tout le temps, je ne savais pas que vous pourriez l'utiliser pour supprimer de la sorte. Vraiment plus rapide que rm -rf
John Powell

22
rsyncpeut être plus rapide que clair rm, car il garantit les suppressions dans le bon ordre, ce qui nécessite moins de recalcul du temps de calcul. Voir cette réponse serverfault.com/a/328305/105902
Marki555

7
Quelqu'un peut-il modifier l'expression perl pour supprimer de manière récursive tous les répertoires et fichiers d'un répertoire_to_be_deleted ?
Abhinav

5
Notes: ajoutez une -Poption à rsync pour un peu plus d’affichage, faites attention à la syntaxe, les barres obliques de fin sont obligatoires. Enfin, vous pouvez lancer la commande rsync une première fois avec l’ -noption de lancer un essai à blanc en premier .
Drasill

1
-aégal -rlptgoD, mais pour la suppression seulement -rdest nécessaire
Koen.

39

Quelqu'un sur Twitter a suggéré d'utiliser -deleteau lieu de-exec rm -f{} \;

Cela a amélioré l'efficacité de la commande, il utilise toujours la récursivité pour tout parcourir.


11
Ceci est non standard. GNU finda -delete, et d'autres findpeut-être.
enzotib

14
-deletedoit toujours être préféré -exec rmlorsque disponible, pour des raisons de sécurité et d’efficacité.
jw013

6
GNU est le standard de facto .
RonJohn

17

Qu'en est-il quelque chose comme: find /path/to/folder -name "filenamestart*" -type f -print0 | xargs -0rn 20 rm -f

Vous pouvez limiter le nombre de fichiers à supprimer en une fois en modifiant l’argument du paramètre -n. Les noms de fichiers avec des blancs sont également inclus.


2
Vous n'avez probablement pas besoin de ce -n 20bit, car xargs devrait de toute façon se limiter à des tailles de liste d'arguments acceptables.
Inutile

Oui, tu as raison. Voici une note de 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. Donc, l' -noption est destinée aux cas où xargs ne peut pas déterminer la taille de la mémoire tampon de la CLI ou si la commande exécutée a des limites.
digital_infinity

12

Pour en revenir à l’un des commentaires, je ne pense pas que vous faites ce que vous pensez faire.

J'ai d'abord créé une énorme quantité de fichiers, pour simuler votre situation:

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

Ensuite, j'ai essayé ce que je m'attendais à échouer et ce que vous faites apparemment à la question:

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

Mais cela ne le travail:

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

6
C’est la seule solution qui a fonctionné: exécuter rm -Rf bigdirectoryplusieurs fois. J'ai eu un répertoire avec des millions de millions de sous-répertoires et de fichiers. Je ne pouvais même pas courir lsou findou rsyncdans ce répertoire, car il a manqué de mémoire. La commande rm -Rfquitte plusieurs fois (mémoire insuffisante) en ne supprimant qu'une partie des milliards de fichiers. Mais après plusieurs tentatives, il a finalement fait le travail. Semble être la seule solution si le manque de mémoire est le problème.
erik

12

Un truc astucieux:

rsync -a --delete empty/ your_folder/

Cela nécessite beaucoup de ressources processeur, mais vraiment très rapide. Voir https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html


Ce n'est pas si rapide, car il lit le contenu du répertoire de manière efficace. Voir cette réponse pour 10x solution plus rapide et explication serverfault.com/a/328305/105902
Marki555

2
@ Marki555: dans l'édition de la question, il est signalé 60 secondes pour rsync -a --deletevs 43 pour lsdent. Le rapport 10x était pour time ls -1 | wc -l vs time ./dentls bigfolder >out.txt(c'est une comparaison partiellement juste en raison de > filevs wc -l).
Hastur

Le problème, c’est que AUCUNE des commandes n’exécute réellement l’opération de traversée souhaitée pour la suppression. Le code qu'ils donnent? NE FONCTIONNE PAS comme décrit par Marki555.
Svartalf

6

J'ai eu l'occasion de tester -deletepar rapport à -exec rm \{\} \;et pour moi -deleteétait la réponse à ce problème.

En utilisant -deletesupprimé les fichiers dans un dossier de 400 000 fichiers au moins 1 000 fois plus rapide que rm.

L'article 'Comment supprimer un grand nombre de fichiers sous Linux' suggère qu'il est environ trois fois plus rapide, mais dans mon test, la différence était beaucoup plus spectaculaire.


3
L'utilisation find -execexécute la rmcommande pour chaque fichier séparément, c'est pourquoi il est si lent.
Marki555

5

A propos de l' -deleteoption ci-dessus: je l'utilise pour supprimer un grand nombre de fichiers (1M + est) dans un dossier temporaire que j'ai créé et que j'ai oublié par inadvertance de nettoyer tous les soirs. J'ai accidentellement rempli mon disque / partition, et rien d'autre ne pouvait les supprimer sauf la find .commande. C'est lent, au début j'utilisais:

find . -ls -exec rm {} \;

Mais cela prenait un temps EXTREME. Cela a commencé après environ 15 minutes pour supprimer certains fichiers, mais j’imagine qu’il en supprimait moins de 10 par seconde environ après le début. Alors, j'ai essayé le:

find . -delete

au lieu de cela, et je le laisse courir maintenant. Il semble fonctionner plus vite, bien que cela pèse EXTRÊMEMENT sur le CPU, contrairement à l’autre commande. Cela fait environ une heure que ça fonctionne et je pense que je vais gagner de la place sur mon disque et que la partition se "maigrit" progressivement, mais cela prend encore beaucoup de temps. Je doute sérieusement que cela fonctionne 1 000 fois plus vite que l'autre. Comme dans toutes choses, je voulais juste souligner le compromis entre l'espace et le temps. Si vous avez la bande passante du processeur à dépenser (nous le faisons), exécutez cette dernière. Il y a mon processeur en marche ( uptimerapports):

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

Et j’ai vu la charge moyenne dépasser 30,00, ce qui n’est pas bon pour un système occupé, mais pour le nôtre, qui est normalement peu chargé, la situation est acceptable pendant quelques heures. J'ai vérifié la plupart des autres éléments du système et ils sont toujours réactifs, donc tout va bien pour le moment.


si vous voulez utiliser, execvous ne voudrez presque certainement pas utiliser -lset do find . -type f -exec rm '{}' ++ est plus rapide car il donnera à rm autant d'arguments qu'il peut gérer à la fois.
Xenoterracide

Je pense que vous devriez aller de l'avant et éditer ceci dans sa propre réponse… c'est vraiment trop long pour un commentaire. En outre, il semble que votre système de fichiers comporte des suppressions assez coûteuses, vous êtes curieux de savoir de quel type il s'agit? Vous pouvez exécuter que find … -deletepar niceou ionice, qui peut aider. Il est donc possible que certaines options de montage soient remplacées par des paramètres moins protégés contre les collisions. (Et, bien sûr, en fonction des éléments mkfs
présents

3
La moyenne de charge n'est pas toujours le processeur, c'est juste une mesure du nombre de processus bloqués au fil du temps. Les processus peuvent bloquer les E / S du disque, ce qui est probablement le cas ici.
Score_Under

Notez également que la moyenne de charge ne tient pas compte du nombre de processeurs logiques. Ainsi, loadavg 1pour une machine monocœur est identique à loadavg 64sur un système 64 cœurs, ce qui signifie que chaque processeur est occupé à 100% du temps.
Marki555


3

Pensez à utiliser le volume Btrfs et supprimez simplement le volume entier pour un tel répertoire avec un grand nombre de fichiers.

Sinon, vous pouvez créer un fichier image FS, puis démonter et supprimer son fichier pour tout supprimer en même temps très rapidement.


2

En supposant que GNU soit parallelinstallé, j'ai utilisé ceci:

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

et c'était assez rapide.


2

Utilisez rm -rf directoryau lieu de rm -rf *.

Au début, nous étions rm -rf *dans le répertoire pour effacer le contenu et nous avons pensé que c'était aussi rapide que possible. Mais l'un de nos ingénieurs expérimentés nous a suggéré d'éviter d'utiliser les astérisques ( *) et de passer plutôt dans le répertoire parent, comme rm -rf directory.

Après de vives discussions sur la manière dont cela ne ferait pas une différence, nous avons décidé de le comparer, avec une troisième méthode d'utilisation find. Voici les résultats:

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

rm -rf directoryest environ 9 fois plus rapide que rm -rf *!

Inutile de dire que nous avons acheté une bière à cet ingénieur !

Nous utilisons donc maintenant rm -rf directory; mkdir directorypour supprimer le répertoire et le recréer.


1

La suppression des VRAI GRANDS répertoires nécessite une approche différente, comme je l’ai appris sur ce site - vous devrez utiliser ionice.Il garantit (avec -c3) que les suppressions ne seront effectuées que lorsque le système dispose d’un délai d’entrée-sortie. La charge de vos systèmes ne sera pas élevée et tout restera réactif (bien que mon temps de calcul pour trouver était assez élevé, à environ 50%).

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

5
utiliser +au lieu de \;rendrait cela plus rapide car il transmet plus d'arguments à la fois, moins de forking
xenoterracide

1
Pourquoi pas ionice -c3 find <dir> -type f -delete
jtgd

1

Si vous avez des millions de fichiers et que chacune des solutions ci-dessus crée un stress dans votre système, essayez cette inspiration:

Fichier 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

Et maintenant, supprimez les fichiers:

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

Find créera des lots (voir getconf ARG_MAX) de quelques dizaines de milliers de fichiers et les transmettra à nice_delete. Cela créera des lots encore plus petits pour permettre de dormir lorsque la surcharge est détectée.


0
ls -1 | xargs rm -rf 

devrait fonctionner dans le dossier principal


1
lsne fonctionnera pas à cause de la quantité de fichiers dans le dossier. C'est pourquoi j'ai dû utiliser find, merci quand même.
Toby

4
@Toby: Essayez ls -f, ce qui désactive le tri. Le tri nécessite que le répertoire entier soit chargé en mémoire pour être trié. Un non trié lsdevrait pouvoir diffuser sa sortie.
camh

1
Ne fonctionne pas sur les noms de fichiers contenant des nouvelles lignes.
maxschlepzig

@camh c'est vrai. Toutefois, la suppression des fichiers dans l'ordre de tri est plus rapide que dans les fichiers non triés (en raison du recalcul de l'arborescence du répertoire après chaque suppression). Voir cette réponse pour un exemple. Serverfault.com/a/328305/105902
Marki555

@maxschlepzig pour de tels fichiers que vous pouvez utiliser find . -print0 | xargs -0 rm, qui utilisera le caractère NULL comme séparateur de nom de fichier.
Marki555

0

Pour l'indice d'Izkata ci-dessus:

Mais cela ne le travail:

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

Cela a presque fonctionné - ou aurait fonctionné - mais j'ai eu quelques problèmes d'autorisation. Les fichiers étaient sur un serveur, mais je ne comprends toujours pas d'où vient ce problème de permission. Quoi qu'il en soit, le terminal a demandé une confirmation pour chaque fichier. Le nombre de fichiers était d’environ 20 000, ce n’était donc pas une option. Après "-r", j'ai ajouté l'option "-f". La commande entière était donc " rm -r -f nomdossier / ". Ensuite, cela a semblé bien fonctionner. Je suis un novice avec Terminal, mais je suppose que c'était d'accord, non? Merci!


0

En fonction de votre capacité à vous débarrasser de ces fichiers, je vous suggère d'utiliser shred.

$ shred -zuv folder

si vous voulez purger le répertoire, mais que vous ne pouvez pas le supprimer et le recréer, je suggère de le déplacer et de le recréer instantanément.

mv folder folder_del
mkdir folder
rm -rf folder_del

croyez-le ou non, ceci est plus rapide, car un seul inode doit être changé. Rappelez-vous: vous ne pouvez pas vraiment paralléliser ce goût sur un ordinateur multicœur. Cela se résume à l'accès au disque, qui est limité par le RAID ou ce que vous avez.


1
shred ne fonctionnera pas avec beaucoup de systèmes de fichiers modernes.

0

Si vous souhaitez simplement vous débarrasser de nombreux fichiers dès que possible, ls -f1 /path/to/folder/with/many/files/ | xargs rmcela fonctionnera sans problème, mais ne le lancez pas sur des systèmes de production, car votre système risque de devenir un problème d'E / S et les applications risquent de rester bloquées lors de l'opération de suppression.

Ce script fonctionne bien pour de nombreux fichiers et ne devrait pas affecter le chargement du système.

#!/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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.