Unix: Comment supprimer des fichiers répertoriés dans un fichier


92

J'ai un long fichier texte avec la liste des masques de fichiers que je souhaite supprimer

Exemple:

/tmp/aaa.jpg
/var/www1/*
/var/www/qwerty.php

J'ai besoin de les supprimer. J'ai essayé rm `cat 1.txt` et la liste est trop longue.

J'ai trouvé cette commande, mais lorsque je vérifie les dossiers de la liste, certains d'entre eux ont encore des fichiers. xargs rm <1.txtL'appel manuel rm supprime les fichiers de ces dossiers, donc aucun problème avec les autorisations.


8
Même si c'est six ans plus tard: cela vous dérangerait-il d'accepter l'une des réponses? Cela marquera la question comme résolue et aidera également les autres utilisateurs.
MERose

Réponses:


114

Ce n'est pas très efficace, mais fonctionnera si vous avez besoin de modèles glob (comme dans / var / www / *)

for f in $(cat 1.txt) ; do 
  rm "$f"
done

Si vous n'avez aucun modèle et êtes sûr que vos chemins dans le fichier ne contiennent pas d'espaces ou d'autres choses bizarres, vous pouvez utiliser des xargs comme ceci:

xargs rm < 1.txt

4
Que se passera-t-il avec la première solution (utilisant CAT) si le chemin contient un espace?
a.dibacco

58

En supposant que la liste des fichiers se trouve dans le fichier 1.txt, procédez comme suit:

xargs rm -r <1.txt

L' -roption provoque une récursivité dans tous les répertoires nommés dans 1.txt.

Si des fichiers sont en lecture seule, utilisez l' -foption pour forcer la suppression:

xargs rm -rf <1.txt

Soyez prudent lorsque vous entrez dans tout outil effectuant des suppressions programmatiques. Assurez certains que les fichiers nommés dans le fichier d'entrée sont vraiment à supprimer. Faites particulièrement attention aux fautes de frappe apparemment simples. Par exemple, si vous entrez un espace entre un fichier et son suffixe, il apparaîtra comme deux noms de fichiers distincts:

file .txt

est en fait deux fichiers distincts: fileet.txt .

Cela peut ne pas sembler si dangereux, mais si la faute de frappe est quelque chose comme ça:

myoldfiles *

Ensuite , au lieu de supprimer tous les fichiers qui commencent par myoldfiles, vous finirez par la suppression myoldfileset tous les fichiers-non et répertoires dans le répertoire courant. Probablement pas ce que vous vouliez.


1
"myoldfiles /" ou "/ tmp" encore plus désastreux avec -rf. Notez le caractère espace à côté du "/".
Ray

23

Utilisez ceci:

while IFS= read -r file ; do rm -- "$file" ; done < delete.list

Si vous avez besoin d'une expansion globale, vous pouvez omettre de citer $file:

IFS=""
while read -r file ; do rm -- $file ; done < delete.list

Mais soyez averti que les noms de fichiers peuvent contenir du contenu "problématique" et j'utiliserais la version non citée. Imaginez ce modèle dans le fichier

*
*/*
*/*/*

Cela en supprimerait beaucoup du répertoire actuel! Je vous encourage à préparer la liste de suppression de manière à ce que les modèles globaux ne soient plus nécessaires, puis à utiliser les guillemets comme dans mon premier exemple.


3
C'est actuellement la seule réponse qui gère toutes /My Dir With Spaces/ /Spaces and globs/*.txt, --file-with-leading-dasheset en prime , il est Posix et fonctionne sur GNU / Linux, BSD et macOS.
cet autre gars

17

Vous pouvez utiliser '\ n' pour définir le caractère de nouvelle ligne comme délimiteur.

xargs -d '\n' rm < 1.txt

Soyez prudent avec le -rf car il peut supprimer ce que vous ne voulez pas si le 1.txt contient des chemins avec des espaces. C'est pourquoi le nouveau délimiteur de ligne est un peu plus sûr.

Sur les systèmes BSD, vous pouvez utiliser l'option -0 pour utiliser des caractères de nouvelle ligne comme délimiteur comme ceci:

xargs -0 rm < 1.txt

1
Sur OS X, ça me donnexargs: illegal option -- d
waldyrious

Bon point. Il semble qu'il n'y ait pas d'autre option que -0sur OS X.
Ray

2
xargs -I_ rm _ fonctionne également sous OS X :) voir stackoverflow.com/a/39335402/266309
waldyrious

Si vous utilisez BSD xargs (là où il n'y en a pas -d), vous pouvez d'abord convertir les nouvelles lignes en nuls:tr '\n' '\0' < 1.txt | xargs -0 rm
OrangeDog

13

Vous pouvez utiliser ce one-liner:

cat 1.txt | xargs echo rm | sh

Ce qui fait l'expansion du shell mais exécute rmle nombre minimum de fois.


Tant qu'une extension n'est pas trop longue?
Douglas Leeder

1
Certes, un glob peut produire une liste d'arguments trop longue - vous pouvez ajouter l' -n <n>argument à xargspour réduire le nombre d'arguments passés à chaque rm, mais cela ne vous protégera toujours pas d'un seul glob qui dépasse la limite.
tgdavies

13

xargs -I{} sh -c 'rm {}' < 1.txtdevrait faire ce que vous voulez. Soyez prudent avec cette commande car une entrée incorrecte dans ce fichier peut causer beaucoup de problèmes.

Cette réponse a été modifiée après que @tdavies ait souligné que l'original ne faisait pas d'expansion du shell.


Merci, mais pas besoin de supprimer des dossiers, seulement des fichiers
Alexander

2
Les globs ne seront pas développés, car ils ne sont jamais «vus» par le shell.
tgdavies

@tdavies wow - vous avez raison. Cette commande (quoique un peu plus moche) fonctionnera:xargs -I{} sh -c 'rm {}' < 1.txt
Mark Drago

4

Dans ce cas particulier, en raison des dangers cités dans d'autres réponses, je voudrais

  1. Modifiez par exemple Vim et :%s/\s/\\\0/g, en échappant tous les caractères d'espace avec une barre oblique inverse.

  2. Ensuite :%s/^/rm -rf /, en ajoutant la commande. Avec, -rvous n'avez pas à vous soucier d'avoir des répertoires répertoriés après les fichiers qu'ils contiennent, et avec -fcela, vous ne vous plaindrez pas en raison de fichiers manquants ou d'entrées en double.

  3. Exécutez toutes les commandes: $ source 1.txt


Les espaces ne sont pas les seuls caractères à échapper, mais les accolades de tout type sont également populaires dans les noms de fichiers, ce qui peut entraîner une expansion indésirable.
emk2203

4

cat 1.txt | xargs rm -f | bash Exécutez la commande fera ce qui suit pour les fichiers uniquement.

cat 1.txt | xargs rm -rf | bash Exécutez la commande fera le comportement récursif suivant.


2

Juste pour fournir un autre moyen, vous pouvez également utiliser simplement la commande suivante

$ cat to_remove
/tmp/file1
/tmp/file2
/tmp/file3
$ rm $( cat to_remove )

1

Voici un autre exemple en boucle. Celui-ci contient également une 'instruction if' comme exemple de vérification pour voir si l'entrée est un 'fichier' (ou un 'répertoire' par exemple):

for f in $(cat 1.txt); do if [ -f $f ]; then rm $f; fi; done

0

Ici , vous pouvez utiliser ensemble de dossiers de deletelist.txt tout en évitant certains modèles aussi bien

foreach f (cat deletelist.txt)
    rm -rf ls | egrep -v "needthisfile|*.cpp|*.h"
end

0

Cela permettra aux noms de fichiers d'avoir des espaces (exemple reproductible).

# Select files of interest, here, only text files for ex.
find -type f -exec file {} \; > findresult.txt
grep ": ASCII text$" findresult.txt > textfiles.txt
# leave only the path to the file removing suffix and prefix
sed -i -e 's/:.*$//' textfiles.txt
sed -i -e 's/\.\///' textfiles.txt

#write a script that deletes the files in textfiles.txt
IFS_backup=$IFS
IFS=$(echo "\n\b")
for f in $(cat textfiles.txt); 
do 
rm "$f"; 
done
IFS=$IFS_backup

# save script as "some.sh" and run: sh some.sh

0

Au cas où quelqu'un préfère sedet supprimer sans extension de caractères génériques :

sed -e "s/^\(.*\)$/rm -f -- \'\1\'/" deletelist.txt | /bin/sh

Rappel: utilisez des chemins absolus dans le fichier ou assurez-vous que vous êtes dans le bon répertoire.

Et pour être complet, la même chose avec awk:

awk '{printf "rm -f -- '\''%s'\''\n",$1}' deletelist.txt | /bin/sh

L'expansion des caractères génériques fonctionnera si les guillemets simples sont supprimés, mais cela est dangereux si le nom de fichier contient des espaces. Cela aurait besoin d'ajouter des guillemets autour des caractères génériques.

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.