Supprimer tous les fichiers d'un répertoire dont le nom ne correspond pas à une ligne d'une liste de fichiers

9

J'ai un répertoire avec plus de 1000 fichiers. Dans un fichier texte, j'ai environ 50 noms de fichiers, un par ligne. Je voudrais supprimer tous les fichiers du répertoire dont les noms de fichiers ne correspondent pas à une entrée de la liste. Quelle est la meilleure façon de procéder? J'ai commencé un script shell, mais je n'ai pas pu déterminer la commande appropriée pour déterminer si le nom de fichier est sur la liste. Merci.

Nathan
la source

Réponses:

8

Je me rends compte que toute question demandant comment supprimer des fichiers doit être prise avec beaucoup de soin. Ma première réponse a été trop hâtive, je n'ai pas pris le fait que la liste de fichiers pouvait être mal formée pour être utilisée avec egrep. J'ai édité la réponse pour réduire ce risque.

Cela devrait fonctionner pour les fichiers qui n'ont pas d'espace dans le nom:

Reconstruisez d'abord votre liste de fichiers pour être sûr de correspondre au nom de fichier exact:

sed -e 's,^,^,' -e 's,$,$,'  filelist  > newfilelist 

construire les commandes rm

cd your_directory
ls | egrep -vf newfilelist   | xargs -n 1 echo rm  >  rmscript

Vérifiez si le script rm vous convient (vous pouvez le faire avec "vim" ou "less").
Effectuez ensuite l'action:

sh -x rmscript

Si les fichiers ont des espaces dans leur nom (si les fichiers ont le "dans le nom, cela ne fonctionnera pas):

ls | egrep -vf newfilelist  | sed 's,^\(.*\)$,rm "\1",' > rmscript

bien sûr, la liste de fichiers ne doit pas se trouver dans le même répertoire!

ÉDITÉ:

La liste des fichiers de Nathan contenait des noms qui correspondaient à tous les fichiers du répertoire (comme "html" correspond à "bob.html"). Donc rien n'a été supprimé car egrep -vfabsorbé tout le flux. J'ai ajouté une commande pour mettre un "^" et un "$" autour de chaque nom de fichier. J'ai eu de la chance ici que la liste de fichiers de Nathan soit correcte. Aurait-il été formaté DOS avec des lignes terminées CR-LF ou avec des espaces supplémentaires, aucun fichier n'aurait été conservé par l'egrep et tout aurait été supprimé.

Emmanuel
la source
Lorsque j'exécute la commande d'aperçu, j'obtiens une ligne avec "rm". Lorsque j'exécute la commande réelle, j'obtiens un message d'erreur sur les arguments manquants pour rm. Ai-je besoin d'une syntaxe spéciale pour utiliser les résultats de ls | egrep dans l'entrée xargs?
Nathan
@Nathan, vous devez d'abord accéder à votre répertoire. Pas de syntaxe spéciale. lsfournit les noms de fichiers du répertoire, egrep -vf filelistfiltrez vos 50 noms de fichiers. J'ai bien peur que vous ayez supprimé tous vos fichiers.
Emmanuel
@Emamanuel J'exécute la commande à partir du répertoire qui contient les fichiers à supprimer.
Nathan
@Nathan, tous vos fichiers ont-ils été supprimés?
Emmanuel
non, ils sont toujours là.
Nathan
1

Préconstruisez les arguments pour find:

{
  read -r
  keep=( -name "$REPLY" ) # no `-o` before the first one.
  while read -r; do
    keep+=( -o -name "$REPLY" )
  done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +

Utilisez les echopièces pour voir ce qui serait construit. Retirez les echopièces pour le faire fonctionner.

Mise à jour: Démonstration:

##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"

##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
 op$

##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +

##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"
kojiro
la source
j'aime mieux celui-ci car il supprime le besoin de la liste de fichiers
eyoung100
+1 de moi, même si cela ne fonctionne pas très bien avec les espaces. Peut-être que quelques guillemets simples ( ') devraient être ajoutés, c'est keep=( -name \'"$REPLY"\' )-à- dire et keep+=( -o -name \'"$REPLY"\' ).
Cristian Ciupitu
ce qui précède est dangereux, car vous pouvez supprimer accidentellement des fichiers.
davidva
@CristianCiupitu n'est-ce pas? J'ai ajouté une démo montrant qu'elle traite très bien les espaces blancs.
kojiro
@davidva Dans quelles circonstances? Chaque fois que vous automatisez la suppression de choses, vous risquez de faire une erreur, mais dans les paramètres de la question, je pense que ma démo prouve que cette approche est bonne.
kojiro
1

Avec zsh:

mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Il lit les lignes d' filelistun tableau et utilise ensuite les qualificatifs glob / echaîne pour glob / sélectionner uniquement les noms de fichiers non présents dans le tableau: le .sélectionne uniquement les fichiers normaux (ajoutez Dsi votre liste contient des fichiers de points) et le négatif annule en ^e_'expression'_outre ne sélectionne que ceux pour dont l'expression renvoie false, c'est-à-dire si leur nom ( $REPLY) n'est pas un élément du tableau .
Si vous êtes satisfait du résultat, remplacez print -rlpar rmpour supprimer réellement les fichiers:

rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Pour sélectionner et supprimer des fichiers de manière récursive, utilisez le modificateur */**glob with ${REPLY:t}glob:

rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)
don_crissti
la source
0

Si vous mettez le contenu du répertoire dans un fichier comme ceci:

cd <somedirectory>
ls >> filelist

Ouvrez la liste de fichiers avec un éditeur de texte et supprimez tous les fichiers sauf ceux que vous souhaitez supprimer . C'est en gras car c'est l'approche opposée à la réponse ci-dessus

Essaye ça:

while read p || [[ -n $p ]]; 
echo $p
done < filelist

Si vous voyez votre liste de fichiers sortie à l'écran, remplacez echo par rm -v, comme ceci:

while read p || [[ -n $p ]]; 
rm -v $p
done < filelist
eyoung100
la source
0

Exécutez le script ci-dessous.

  1. Au départ, je trouve tous les fichiers qui sont présents dans le répertoire et stocke la sortie dans un autre fichier all_files.
  2. Nous avons un fichier qui contient la liste des fichiers qui ne doivent PAS être supprimés ( not_to_be_deleted_files).
  3. J'ajoute les noms de fichiers not_to_be_deleted_fileset files_to_be_deletedà la fin not_to_be_deleted_filescar nous avons besoin de ces 2 fichiers.
  4. Maintenant, je trouve les fichiers qui doivent être supprimés en utilisant la joincommande linux et en redirigeant la sortie vers le files_to_be_deleted fichier.
  5. Maintenant, dans la boucle while finale, je lis tous les noms de fichiers files_to_be_deletedet je supprime les fichiers mentionnés dans ce nom de fichier.

Le script est comme ci-dessous.

find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) >   files_to_be_deleted
while read file
rm  "$file"
done < files_to_be_deleted

PS : Probablement, si vous souhaitez que cela soit enregistré en tant que script et l'exécutez, vous pouvez également ajouter le nom du script en utilisant echo scriptname >> not_to_be_deleted_files.

Bien que ce ne soit pas obligatoire, je préfère le faire car il n'y aura aucun regret plus tard. J'ai testé un petit ensemble de fichiers et cela a fonctionné dans mon système. Cependant, si vous voulez en être sûr, essayez d'abord dans un testrépertoire, puis supprimez les fichiers dans le répertoire d'origine.

Ramesh
la source
0
  • Utilisez la liste comme source pour déplacer tous les fichiers de la liste vers un nouveau répertoire de sauvegarde vide.
  • Comparez le nombre de fichiers dans la liste et le nombre de fichiers enregistrés.
  • Si les deux correspondent, supprimez tous les fichiers non enregistrés avec votre méthode préférée.
  • Reculez les fichiers enregistrés.
Utilisateur inconnu
la source
0

J'ai opté pour une approche plus sûre et beaucoup plus rapide car j'avais 18 000 fichiers dans la liste! J'avais besoin de nettoyer des images dans une grande installation Drupal.

La suppression de tous les fichiers qui ne figurent pas dans la liste revient à ne conserver que ceux qui figurent dans la liste. J'ai donc décidé de copier les fichiers de la liste vers un autre emplacement, mais copier 20 Go de fichiers prendrait trop de place et serait très lent également. L'astuce consiste donc à copier les fichiers sous hardlinks, à la place, en utilisant l' -loption de cp. Cela ne prend presque pas de place et est très rapide. De plus, comme j'avais besoin de conserver la structure du répertoire, j'ai utilisé l' --parentsoption.

Voici un extrait de ma liste de fichiers:

1px.png
misc/feed.png
modules/file/icons/x-office-presentation.png
modules/file/icons/x-office-spreadsheet.png
newsletter.png
sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png
sites/all/libraries/ckeditor/plugins/smiley/images/regular_smile.png
sites/default/files/009313_PwC_banner_CBS_Observer_180x246px.jpg

Ainsi, un exemple de ligne serait, avec temp étant la destination:

cp -l --parents 'misc/feed.png' temp

Cela va créer cette structure:

temp
  misc
    feed.png

Notez que la destination doit être dans le même système de fichiers que la source pour que les liens physiques fonctionnent.

L'étape suivante consiste à construire le script:

sed -e "s,^,cp -l --parents '," -e "s,$,' /some/where/temp," filelist > newfilelist

Maintenant, en supposant que vous avez déjà créé le répertoire vide / certains / où / temp, vous pouvez copier les fichiers comme ceci:

sh newfilelist 2> missing_files

Notez comment les erreurs se terminent missing_files. Le bonus supplémentaire de cette approche est que vous obtiendrez une liste de fichiers de la liste d'origine qui n'existent pas !

Après avoir exécuté le script, temp ne contiendra que les fichiers qui se trouvent dans la liste des fichiers, mais sans rien supprimer et sans prendre d'espace supplémentaire. Si vous êtes satisfait du résultat, vous pouvez supprimer tous les fichiers originaux, y compris les sous-dossiers.

Enfin, déplacez les fichiers et dossiers de temp vers leur emplacement d'origine.

Pour les 18 000 fichiers, cela n'a pris que quelques secondes.

marlar
la source
0

Sûr, simple.

cd dans le répertoire.

Créez un répertoire temporaire.

mv *.yourExlusionSelector.* ./temp
rm *
mv ./temp ./
rm -rf ./temp

terminé.

paradisaeidae
la source
Bienvenue sur le site. Bien que votre approche fonctionne si les noms sur la liste mentionnée par l'OP sont le résultat d'une simple correspondance de modèle - ce qui peut très bien être le cas - veuillez noter que l'OP a déclaré que les noms de fichiers à exclure sont stockés dans un fichier spécifique; vous souhaiterez peut-être développer votre réponse afin de lire les modèles d'exclusion de ce fichier au lieu de vous fier à un modèle statique ou de devoir copier-copier potentiellement plusieurs modèles sur la console.
AdminBee