web-dev-qa-db-fra.com

Supprimez efficacement un grand répertoire contenant des milliers de fichiers

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

Il y a tellement de fichiers qui exécutent rm -rf renvoie une erreur et à la place ce que nous devons faire est quelque chose comme:

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

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

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

177
Toby

L'utilisation de rsync est étonnamment simple et rapide.

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

La réponse de @ sarath a mentionné 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
238
stevendaniels

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

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

42
Toby

Qu'en est-il de 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 simultanément en modifiant l'argument du paramètre -n. Les noms de fichiers avec des blancs sont également inclus.

19
digital_infinity

Une astuce astucieuse:

rsync -a --delete empty/ your_folder/

C'est super intensif en CPU, 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

16
MZAweb

Pour approfondir 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 dans la question:

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

Mais cela fonctionne fonctionne:

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

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

En utilisant -delete a supprimé les fichiers d'un dossier de 400 000 fichiers au moins 1 000 fois plus vite 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 dramatique.

10
user2365090

À propos de -delete option 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 rempli mon disque/partition accidentellement et rien d'autre ne pouvait les supprimer, sauf le find . commande. C'est lent, au début j'utilisais:

find . -ls -exec rm {} \;

Mais cela prenait un temps EXTRÊME. Il a commencé après environ 15 minutes pour supprimer certains fichiers, mais je suppose qu'il supprimait moins de 10 environ par seconde après avoir finalement commencé. J'ai donc essayé:

find . -delete

à la place, et je le laisse s'exécuter maintenant. Il semble fonctionner plus rapidement, bien qu'il soit extrêmement lourd sur le processeur, ce que l'autre commande n'était pas. Cela fonctionne depuis une heure maintenant et je pense que je récupère de l'espace sur mon lecteur et que la partition "s'amincit progressivement", mais cela prend encore très longtemps. Je doute sérieusement qu'il tourne 1 000 fois plus vite que l'autre. Comme en toutes choses, je voulais juste souligner le compromis entre l'espace et le temps. Si vous avez la bande passante CPU à épargner (nous le faisons), exécutez cette dernière. Mon processeur fonctionne (uptime rapports):

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

Et j'ai vu la moyenne de charge dépasser 30,00, ce qui n'est pas bon pour un système occupé, mais pour le nôtre qui est normalement légèrement chargé, c'est OK pendant quelques heures. J'ai vérifié la plupart des autres choses sur le système et elles sont toujours réactives donc nous sommes OK pour l'instant.

5
Scotty

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

Alternativement, vous pouvez créer un fichier image FS puis démonter et supprimer son fichier pour tout supprimer à la fois très rapidement.

4
Sergei

Utilisation rm -rf directory au lieu de rm -rf *.

Nous faisions initialement rm -rf * dans le répertoire pour effacer le contenu et j'ai pensé que c'était aussi rapide que possible. Mais alors, l'un de nos ingénieurs principaux a suggéré d'éviter d'utiliser les astérisques (*) et passez à la place dans le répertoire parent, comme rm -rf directory.

Après un débat intense sur la façon dont cela ne ferait pas de différence, nous avons décidé de le comparer, ainsi qu'une troisième méthode d'utilisation de find. Voici les résultats:

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

rm -rf directory est environ 9 fois plus rapide que rm -rf *!

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

Alors maintenant, nous utilisons rm -rf directory; mkdir directory pour supprimer le répertoire et le recréer.

4
Joshua Pinter

Il existe quelques méthodes qui peuvent être utilisées pour supprimer un grand nombre de fichiers sous linux. Vous pouvez utiliser l'option find with delete, qui est plus rapide que l'option exec. Ensuite, vous pouvez utiliser Perl unlink, puis même rsync. Comment supprimer un grand nombre de fichiers sous linux

4
sarath

En supposant que GNU parallel installé, j'ai utilisé ceci:

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

et c'était assez rapide.

2
Nacho

La suppression des répertoires VRAIMENT LARGES nécessite une approche différente, comme je l'ai appris de ce site - vous devrez utiliser ionice. Cela garantit (avec -c3) que les suppressions ne seront effectuées que lorsque le système aura IO- le temps pour cela. La charge de vos systèmes n'augmentera pas à un niveau élevé et tout reste réactif (bien que mon temps processeur pour la recherche ait été assez élevé à environ 50%).

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

Si vous avez des millions de fichiers et que chaque solution ci-dessus met votre système à rude épreuve, 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 {} \+

La recherche créera des lots (voir getconf ARG_MAX) de quelques dizaines de milliers de fichiers et passez-le à Nice_delete. Cela créera des lots encore plus petits pour permettre de dormir lorsqu'une surcharge est détectée.

1
brablc

Selon la façon dont vous devez vous débarrasser de ces fichiers, je vous suggère d'utiliser shred.

$ shred -zuv folder

si vous voulez purger le répertoire, mais vous ne pouvez pas le supprimer et le recréer, je vous 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, c'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 revient à l'accès au disque, qui est limité par le RAID ou ce que vous avez.

0
polemon

Les scripts Python ne doivent pas être évités comme impurs:

#!/usr/bin/python3

import shutil
path_for_deletion = input( 'path of dir for deletion> ' ) 
print( 'about to remove ' + path_for_deletion + ' ...' )
shutil.rmtree( path_for_deletion, ignore_errors=True )
print( '... done' )

J'ai demandé au gars qui a fait un benchmarking utile de diverses méthodes ici s'il pouvait essayer de le comparer. D'après mes expériences, cela semble plutôt bien.

NB les erreurs pourraient être gérées pour au moins les imprimer ... mais il pourrait être plus simple d'exécuter trash myDirectoryForDeletion ou rm -rfv myDirectoryForDeletion ensuite.

0
mike rodent

Si vous voulez simplement vous débarrasser de nombreux fichiers dès que possible ls -f1 /path/to/folder/with/many/files/ | xargs rm peut fonctionner correctement, mais il vaut mieux ne pas l'exécuter sur les systèmes de production, car votre système pourrait devenir IO problèmes et applications pourraient se bloquer pendant l'opération de suppression.

Ce script fonctionne bien pour de nombreux fichiers et ne devrait pas affecter l'ioload 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
0
Leon Kramer

Pour l'indice d'Izkata ci-dessus:

Mais cela fonctionne :

$ 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 de permission; les fichiers étaient sur un serveur, mais je ne comprends toujours pas d'où vient ce problème d'autorisation. Quoi qu'il en soit, Terminal a demandé une confirmation sur chaque fichier. Le nombre de fichiers était d'environ 20 000, donc ce n'était pas une option. Après "-r", j'ai ajouté l'option "-f", donc toute la commande était "rm -r -f foldername / = ". Ensuite, cela a semblé bien fonctionner. Je suis novice avec Terminal, mais je suppose que ça allait, non? Merci!

0
user41527
ls -1 | xargs rm -rf 

devrait fonctionner dans le dossier principal

0
PsyStyle

Utilisation ls -f | xargs -n 5000 rm, tout en ajustant le -n pour la taille de lot appropriée à votre système (bravo à @digital_infinity pour -n pointe).

De plus, vous pouvez filtrer la liste avec un grep en ligne, par exemple ls -f | grep '^156' | xargs -n 5000 rm.

D'après mon expérience, c'est beaucoup plus rapide que les techniques utilisant find et évite le besoin de scripts Shell plus complexes.

0
buckaroo1177125