Supprimer tous les fichiers sauf les fichiers avec l'extension pdf dans un répertoire

50

J'ai un répertoire qui contient les éléments suivants:

x.pdf
y.zip
z.mp3
a.pdf

Je veux supprimer tous les fichiers sauf x.pdfet a.pdf. Comment est-ce que je fais ceci du terminal? Il n'y a pas de sous-répertoires, aucune récursivité n'est donc nécessaire.

Nu comme un ver
la source

Réponses:

63
cd <the directory you want>
find . -type f ! -iname "*.pdf" -delete
  • La première commande vous mènera au répertoire dans lequel vous voulez supprimer vos fichiers
  • La deuxième commande supprimera tous les fichiers sauf ceux qui se terminent par .pdfde nom de fichier

Par exemple, s’il existe un répertoire appelé tempdans votre dossier personnel:

cd ~/temp

puis supprimez les fichiers:

find . -type f ! -iname "*.pdf" -delete

Cela supprimera tous les fichiers sauf xyz.pdf.

Vous pouvez combiner ces deux commandes pour:

find ~/temp -type f ! -iname "*.pdf" -delete

.est le répertoire en cours. !signifie prendre tous les fichiers sauf ceux avec .pdfà la fin. -type fsélectionne uniquement les fichiers, pas les répertoires. -deletesignifie le supprimer.

REMARQUE: cette commande supprimera tous les fichiers (sauf les fichiers pdf mais les fichiers cachés) du répertoire actuel ainsi que de tous les sous-répertoires. !doit venir avant -name. -nameinclura simplement seulement .pdf, tout -inameen incluant à la fois .pdfet.PDF

Pour supprimer uniquement dans le répertoire actuel et non dans les sous-répertoires, ajoutez -maxdepth 1:

find . -maxdepth 1 -type f ! -iname "*.pdf" -delete
Edward Torvalds
la source
Merci d'avoir répondu. Pouvez-vous m'aider à comprendre un peu la syntaxe? .signifie "et"? !signifie "sauf" -namesignifie que vous voulez exclure par un paramètre de nom et que -deletel'action est-elle à entreprendre lors de la recherche? Donc, il recherche tout sauf "* .pdf" et les supprime? Ou ai-je mal compris?
jessenorton
.signifie répertoire en cours. !signifie prendre tous les fichiers sauf celui avec .pdfà la fin. -deletesignifie le supprimer. suis-je clair maintenant?
Edward Torvalds
@terdon Starkers a déclaré qu'il n'y avait pas de sous-répertoires.attendrais de modifier ma réponse pour qu'elle soit plus large
Edward Torvalds
+1 Vous devriez avoir inclus le -maxdepth 1paramètre pour commencer. Ensuite, suggérez de supprimer le paramètre au cas où vous voudriez le supprimer récursivement.
Tulains Córdova
3
cela a attiré mon attention sur le fait que nous devrions utiliser à la -inameplace des -namefichiers avec lesquels l' .PDFextension risque de glisser.
muru
43

Avec bashle shell globing étendu, vous pouvez supprimer tous les fichiers avec des extensions autres que l’ .pdfutilisation de

rm -- *.!(pdf)

Comme indiqué par @pts, les --caractères indiquent la fin de toutes les options de commande; sécurisez la commande dans le cas très rare de fichiers dont le nom commence par un -caractère.

Si vous souhaitez supprimer des fichiers sans extension ainsi que ceux avec des extensions autres que .pdf, alors, comme l'a souligné @DennisWilliamson, vous pouvez utiliser

rm -- !(*.pdf)

La navigation étendue doit être activée par défaut, mais sinon, vous pouvez le faire en utilisant

shopt -s extglob

Surtout si vous souhaitez utiliser ceci dans un script, il est important de noter que si l'expression ne correspond à rien (c'est-à-dire s'il n'y a pas de fichiers non-pdf dans le répertoire), alors par défaut, le glob sera passé non développé au rmcommande, entraînant une erreur comme

rm: cannot remove `*.!(pdf)': No such file or directory

Vous pouvez modifier ce comportement par défaut à l'aide de l' nullgloboption shell, qui a toutefois son propre problème. Pour une discussion plus approfondie, voir NullGlob - Wiki de Greg

Steeldriver
la source
Meilleure approche IMO.
Takkat
Qu'en est-il des fichiers sans extension? FWIW, en zsh c'estrm *~*.pdf
Emil Jeřábek
1
Je mettrais le point entre parenthèses.
Dennis Williamson
4
Ah, l'astérisque devrait aussi aller à l' intérieur: !(*.py). Aussi, vraisemblablement, si l’opérateur ne veut que des fichiers ".pdf", les fichiers sans extension doivent également être supprimés et non ignorés.
Dennis Williamson
1
Cette approche est plus simple et plus nette que la réponse acceptée.
Peter
18

Supprimer dans la corbeille :

$ cd <the directory you want>
$ gvfs-trash !(*.pdf)

Ou via mvcommande (mais de cette façon, vous ne pouvez pas le restaurer depuis la Corbeille car il n'enregistre pas les informations .trashinfo, cela signifie donc que vous avez déplacé vos fichiers vers une destination où il se trouve comme suit).

mv !(*.pdf) ~/.local/share/Trash/files
αғsнιη
la source
6
Cette approche est beaucoup plus sûre que l’utilisation directe rm.
Seth
14

La méthode la plus simple: créez un autre répertoire quelque part (si vous ne supprimez que dans un répertoire, pas de manière récursive, il peut même s'agir d'un sous-répertoire); déplacez tous les fichiers .pdf là-bas; supprimer tout le reste; déplacez le pdf en arrière; supprimer le répertoire intermédiaire.

Rapide, facile, vous pouvez voir exactement ce que vous faites. Assurez-vous simplement que le répertoire intermédiaire se trouve sur le même périphérique que le répertoire que vous êtes en train de nettoyer afin que les déplacements soient renommés, et non des copies!

Jerry
la source
4
+1 Encore une fois pour un commentaire qui ait du sens pour l'utilisateur novice, cela n'entraînera presque certainement pas la suppression de fichiers par inadvertance.
trognanders
4

Utilisez GLOBIGNORE de bash:

GLOBIGNORE=x.pdf:a.pdf
rm *
unset GLOBIGNORE

De la page de manuel de bash:

GLOBIGNORE:

            Une liste de motifs séparés par deux points définissant l'ensemble
            des noms de fichiers à ignorer par l’extension du nom de chemin.

Un test rapide:

mkdir /tmp/foooooo
cd /tmp/foooooo
touch x.pdf y.zip z.mp3 a.pdf
GLOBIGNORE=x.pdf:a.pdf
ls -1 *

Sortie:

y.zip
z.mp3
Cyrus
la source
3

Soyez prudent et composez: utilisez xargs

Voici une approche que j’aime bien, car cela me permet d’être très prudent: composez un moyen d’afficher uniquement les fichiers que je veux supprimer, puis envoyez-les à l’ rmutilisation xargs. Par exemple:

  • ls me montre tout
  • ls | grep pdfme montre les fichiers que je veux garder. Hmm.
  • ls | grep -v pdfmontre le contraire: tout sauf ce que je veux garder. En d'autres termes, il affiche la liste des choses que je veux supprimer. Je peux le confirmer avant de faire quoi que ce soit de dangereux.
  • ls | grep -v pdf | xargs rmenvoie exactement cette liste rmpour suppression

Comme je l'ai dit, j'aime principalement ceci pour la sécurité qu'il procure: ce n'est pas un hasard rm *pour moi. Deux autres avantages:

  • C'est composable; vous pouvez utiliser lsou findpour obtenir la liste initiale, comme vous préférez. Vous pouvez utiliser tout ce que vous voulez pour réduire cette liste - une autre grep, une partie awkou autre. Si vous ne devez supprimer que les fichiers dont le nom contient une couleur, vous pouvez le créer de la même manière.
  • Vous pouvez utiliser chaque outil pour son objectif principal. Je préfère utiliser findpour trouver et rmpour supprimer, plutôt que d'avoir à retenir que findaccepte un -deletedrapeau. Et si vous faites cela, encore une fois, vous pouvez composer des solutions alternatives. peut-être au lieu de rm, vous pourriez créer une trashcommande qui déplace le fichier dans la corbeille (en autorisant "undeletion") et le redirige vers elle au lieu de rm. Vous n'avez pas besoin de findsupporter cette option, il vous suffit de la suivre .

Mise à jour

Voir les commentaires de @pabouk pour savoir comment modifier ceci afin de traiter certains cas de bords, tels que les sauts de ligne dans les noms de fichiers, les noms de fichiers tels que my_pdfs.zip, etc.

Nathan Long
la source
4
J'ai remarqué trois problèmes ici: a) Cela exclura tout fichier contenant pdfn'importe où dans son nom. --- b) Il supprimera les fichiers PDF si l’une des lettres du suffixe est en majuscule. --- c) Ce n'est pas une bonne idée d'utiliser la sortie de ls. Cela ne fonctionnera pas avec les noms de fichiers contenant des nouvelles lignes. Certaines implémentations de lsremplacement de caractères spéciaux, par exemple tabulation par ?. --- Il est préférable d'utiliser: find -maxdepth 1 -print0. (pas si court que ls:) ----- Pour résoudre a) et b) utilisez la grep -vi '\.pdf$'solution --- complète (seulement GNU):find -maxdepth 1 -print0 | grep -viz '\.pdf$' | xargs -0 rm
pabouk
1
Je comprends que vous envisagiez la solution comme un processus "interactif" avec de multiples itérations manuelles, mais les vérifications seront difficilement utilisables pour de longues listes de fichiers et les problèmes mentionnés ci-dessus pourraient facilement faire oublier les erreurs.
pabouk
1
@pabouk bons points; le monde réel complique toujours les choses et vos corrections sont utiles. :) Mais je pense toujours que cette approche globale est la meilleure. S'il y a trop de fichiers pour tout confirmer visuellement, vous pouvez | head -20au moins voir si cela semble assez correct, alors que si vous venez rm my_pattern, vous n'avez aucune chance de repérer une grosse erreur.
Nathan Long
1
Vous pouvez également trouver les fichiers avant de les supprimer, laissez de côté le -delete et utilisez-le simplement find . -type f ! -name "*.pdf"pour imprimer sur la console, ou diriger vers moins ou un fichier. [et puis pipe à xargs à rm si désiré, comme les commentaires de pabouk (avec -print0 | ... -0 pour les noms de fichiers étranges)]
Xen2050
3

Je résous généralement ces problèmes à partir de l'interpréteur interactif Python:

mic@mic ~ $ python
>>> import os
>>> for f in os.listdir('.'):
...   if not f.endswith('.pdf'):
...     os.remove(f)

C'est peut-être plus long qu'un one-line avec findou xargs, mais c'est extrêmement résistant, et je sais exactement ce que ça fait, sans avoir à faire des recherches au préalable.

mic_e
la source
Pour ceux qui deviennent de plus en plus nerveux à chaque nouvelle ligne, nous pourrions en faire une:for item in [f for f in os.listdir('.') if not f.endswith('.pdf')]: os.remove(item)
Jacob Vlijm
python -c "import os; for f in os.listdir('.'): if not f.endswith('.pdf'): os.remove(f)"
mic_e
[os.remove(f) for f in os.listdir('.') if not f.endswith('.pdf')]
mic_e
agréable! le second me donne une erreur de syntaxe, ne vois pas pourquoi.
Jacob Vlijm
étrange; cela fonctionne avec python 3.4 et python 2.7 sur mon système.
mic_e
2

meilleure réponse (par rapport à ma réponse précédente) à cette question sera en utilisant la puissante filecommande.

$ file -i abc.pdf
abc: application/pdf; charset=binary

maintenant votre problème:

cd <the directory you want to search in>
for var in ./*
do
if file -i "$var" | grep -q 'application/pdf\;'
then
echo "$var"
fi
done

le travail de forcommande est de donner les fichiers du répertoire courant sous forme de variable $var. if-thenLa commande affiche le nom des fichiers pdf en prenant le statut de sortie 0de la file -i "$var" | grep -q 'application/pdf\;'commande from , elle ne donnera le statut de sortie 0que si elle trouve des fichiers pdf.

Edward Torvalds
la source
1
rm $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')

Attention! Mieux vaut essayer d'abord

ls -l $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')
Martín-Blas Pérez Pinilla
la source
2
Cela fait défaut à bien des égards: smallo.ruhr.de/award.html#ls , smallo.ruhr.de/award.html#grep , et il ignore totalement les noms de fichiers contenant des espaces ou des caractères spéciaux.
David Foerster
1
Vous devriez vraiment utiliser -iavec greppour la correspondance insensible à la casse.
Muru
1
rm -i -- !(*@(a|x).pdf)

Lire en tant que, supprimez tous les fichiers qui ne sont pas a.pdfou x.pdf.

Cela fonctionne en utilisant des 2 globs étendues, l'extérieur !()pour annuler le glob contenu qui lui - même exige que le glob doit correspondre à un ou plusieurs aou xmodèles avant le .pdfsuffixe. Voir glob # extglob .

$ ls -a
.dotfile1 .dotfile2 a.pdf x.pdf y.zip z.mp3

$ echo -- !(a.pdf)
-- x.pdf y.zip z.mp3

$ echo -- !(x.pdf)
-- a.pdf y.zip z.mp3

$ echo -- !(a.pdf|x.pdf)
-- y.zip z.mp3

$ echo -- !(@(a|x).pdf)   # NOTE.that this matches the .dotfiles* as well
-- . .. .dotfile1 .dotfile2 y.zip z.mp3

$ echo -- !(*@(a|x).pdf)  # but this doesn't
-- y.zip z.mp3

$ echo rm -i -- !(*@(a|x).pdf)
rm -i -- y.zip z.mp3
Shalomb
la source
1

manière de coque portable

$ ksh -c 'for i in ./*; do case $i in *.pdf)continue;; *)rm "$i";; esac;done'

Quasiment POSIX et compatible avec tout style shell Bourne ( ksh, bash, dash). Parfaitement adapté aux scripts portables et lorsque vous ne pouvez pas utiliser bashle globbing de shell étendu.

perl:

$ perl -le 'opendir(my $d,"."); foreach my $f (grep(-f && !/.pdf/ , readdir($d))){unlink $f};closedir $d'                                                             

Ou légèrement plus propre:

$ perl -le 'opendir(my $d,"."); map{ unlink $_ } grep(-f "./$_" && !/.pdf/ , readdir($d));closedir $d'

python alternatif

python -c 'import os;map(lambda x: os.remove(x), filter(lambda x: not x.endswith(".pdf"),os.listdir(".")))'
Sergiy Kolodyazhnyy
la source
0

Faites attention à ce que vous supprimez!

Un moyen sûr de le tester avant d’essayer de supprimer est de commencer par tester ls, car certains comportements non détectés pourraient supprimer des fichiers non souhaités. Et vous pouvez le faire directement en dehors du répertoire. lsest similaire à rm, donc:

ls sub/path/to/files/!(*.pdf)

Cela va lister

y.zip
z.mp3

Et maintenant, vous pouvez voir ce que vous supprimez et pouvez les supprimer en toute sécurité:

rm sub/path/to/files/!(*.pdf)

Et c'est tout. Vous pouvez utiliser un caractère générique *pour être plus sélectif, comme garder uniquement les documents de cours de programmation:

rm sub/path/to/files/!(*programming*)
KeitelDOG
la source