web-dev-qa-db-fra.com

Supprimer tous les fichiers sauf le plus récent script 3 en bash

Question: Comment effacez-vous tous les fichiers d'un répertoire sauf le plus récent des 3?

Trouver les 3 derniers fichiers est simple:

ls -t | head -3

Mais je dois trouver tous les fichiers sauf les 3 plus récents. Comment puis-je faire, et comment puis-je supprimer ces fichiers dans la même ligne sans créer une boucle for inutile pour cela?

J'utilise Debian Wheezy et les scripts bash pour cela.

13
bytecode77

Ceci listera tous les fichiers sauf les trois plus récents:

ls -t | tail -n +4

Cela supprimera ces fichiers:

ls -t | tail -n +4 | xargs rm --

Cela listera aussi les fichiers de points:

ls -At | tail -n +4

et supprimer avec les fichiers de points:

ls -At | tail -n +4 | xargs rm --

Attention, l'analyse ls peut être dangereuse lorsque les noms de fichiers contiennent des caractères amusants tels que des lignes ou des espaces. Si vous êtes certain que vos noms de fichiers ne contiennent pas de caractères amusants, l'analyse ls est relativement sûre, et encore plus s'il s'agit d'un script unique.

Si vous développez un script pour un usage répété, vous ne devez certainement pas analyser la sortie de ls et utiliser les méthodes décrites ici: http://mywiki.wooledge.org/ParsingLs

37
lesmana

Ce qui suit semble un peu compliqué, mais il est très prudent d’être correct, même avec des noms de fichiers inhabituels ou intentionnellement malveillants. Malheureusement, cela nécessite des outils GNU:

count=0
while IFS= read -r -d ' ' && IFS= read -r -d '' filename; do
  (( ++count > 3 )) && printf '%s\0' "$filename"
done < <(find . -maxdepth 1 -type f -printf '%T@ %P\0' | sort -g -z) \
     | xargs -0 rm -f --

Expliquant comment cela fonctionne:

  • La recherche émet <mtime> <filename><NUL> pour chaque fichier du répertoire en cours.
  • sort -g -z effectue un tri numérique général (virgule flottante, par opposition à un entier) basé sur la première colonne (fois) avec les lignes séparées par des NUL.
  • La première read de la boucle while supprime le paramètre mtime (n'est plus nécessaire une fois que sort est terminé).
  • La seconde read dans la boucle while lit le nom du fichier (jusqu'à la NUL).
  • La boucle incrémente, puis vérifie, un compteur; si l'état du compteur indique que nous avons dépassé le saut initial, nous imprimons le nom de fichier, délimité par un NUL.
  • xargs -0 ajoute ensuite ce nom de fichier dans la liste argv qu'il collecte pour appeler rm avec.
9
Charles Duffy
ls -t | tail -n +4 | xargs -I {} rm {}

Si vous voulez un 1 liner

6
Michael Ballent

C'est une combinaison des réponses de Ceving et d'Anubhava… Les deux solutions ne fonctionnent pas pour moi. Parce que je recherchais un script qui devrait être exécuté quotidiennement pour sauvegarder des fichiers dans une archive, je voulais éviter les problèmes avec ls (quelqu'un aurait pu enregistrer un fichier de nom amusant dans mon dossier de sauvegarde). J'ai donc modifié les solutions mentionnées pour répondre à mes besoins. La solution de Ceving supprime les trois fichiers les plus récents - pas ce dont j'avais besoin et on m'a demandé. 

Ma solution supprime tous les fichiers, sauf les trois fichiers les plus récents.

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- |
xargs rm

Quelques explications: 

find liste tous les fichiers (pas les répertoires) du dossier actuel. Ils sont imprimés avec des horodatages.
sort trie les lignes en fonction de l'horodatage (la plus ancienne en haut).
head imprime les lignes du haut, jusqu'aux 3 dernières lignes.
cut supprime les horodatages.
xargs exécute rm pour chaque fichier sélectionné.

A vous de vérifier ma solution:

(
touch -d "6 days ago" test_6_days_old
touch -d "7 days ago" test_7_days_old
touch -d "8 days ago" test_8_days_old
touch -d "9 days ago" test_9_days_old
touch -d "10 days ago" test_10_days_old
)

Cela crée 5 fichiers avec des horodatages différents dans le dossier actuel. Exécutez ceci en premier et le code de suppression pour tester le code.

4
flohall

En zsh:

rm /files/to/delete/*(Om[1,-4])

Si vous souhaitez inclure dotfiles , remplacez la partie entre parenthèses par (Om[1,-4]D).

Je pense que cela fonctionne correctement avec des caractères arbitraires dans les noms de fichiers (juste vérifié avec newline).

Explication: Les parenthèses contiennent des qualificatifs de Glob. O signifie "ordre par, décroissant", m signifie mtime (voir man zshexpn pour d'autres clés de tri - grande page de manuel; recherchez "être trié"). [1,-4] renvoie uniquement les correspondances à l'index basé sur une base 1 à (last + 1 - 4) (notez le -4 pour supprimer tout sauf 3).

1
FunctorSalad

N'utilisez pas ls -t car il est dangereux pour les noms de fichiers pouvant contenir des espaces ou des caractères globaux spéciaux.

Vous pouvez le faire en utilisant tous les utilitaires basés sur gnu pour supprimer tous les fichiers sauf le plus récent dans le répertoire actuel:

find . -maxdepth 1 -type f -printf '%T@\t%p\0' |
sort -z -nrk1 |
tail -z -n +4 |
cut -z -f2- |
xargs -0 rm -f --
1
anubhava

Ceci utilise find au lieu de ls avec une transformation Schwartzian

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g |
tail -3 |
cut -d $'\t' -f 2-

find recherche les fichiers et les décore avec un horodatage et utilise la tabulatrice pour séparer les deux valeurs. sort divise l'entrée par la tabulatrice et effectue un tri numérique général qui trie correctement les nombres à virgule flottante. tail devrait être évident et cut undecorates.

Le problème avec les décorations en général est de trouver un délimiteur approprié, qui ne fait pas partie de l’entrée, les noms de fichiers. Cette réponse utilise le caractère NULL.

0
ceving
ls -t | tail -n +4 | xargs -I {} rm {}

La réponse de Michael Ballent fonctionne mieux comme 

ls -t | tail -n +4 | xargs rm --

jetez-moi une erreur si j'ai moins de 3 fichiers

0
klesk44