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

Question: Comment supprimez-vous tous les fichiers d’un répertoire, à l’exception du dernier 3?

Trouver les 3 fichiers les plus récents est simple:

ls -t | head -3 

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

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

    Cela 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 dotfiles:

     ls -At | tail -n +4 

    et supprimer avec des fichiers dot:

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

    Mais méfiez-vous: l’parsing de ls peut être dangereuse lorsque les noms de fichiers contiennent des caractères amusants tels que des nouvelles lignes ou des espaces. Si vous êtes certain que vos noms de fichiers ne contiennent pas de caractères amusants, alors l’parsing syntaxique de ls est encore plus sûre, même s’il s’agit d’un script unique.

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

    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, il 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:

    • Find émet pour chaque fichier du répertoire en cours.
    • sort -g -z effectue un sorting numérique général (à virgule flottante, par opposition à un entier) basé sur la première colonne (times) avec les lignes séparées par NUL.
    • La première read dans la boucle while supprime le mtime (qui n'est plus nécessaire après le sort ).
    • La seconde read dans la boucle while lit le nom de fichier (en cours d'exécution jusqu'au NUL).
    • La boucle s'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 alors ce nom de fichier dans la liste argv qu'il collecte pour appeler rm avec.
     ls -t | tail -n +4 | xargs -I {} rm {} 

    Si vous voulez un liner

    Ceci est une combinaison de la réponse de ceving et anubhava. Les deux solutions ne fonctionnent pas pour moi. Comme je cherchais un script qui devrait être exécuté chaque jour 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 a été demandé.

    Ma solution supprime tous les fichiers, à l’ exception des 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 répertorie tous les fichiers (pas les répertoires) dans le dossier en cours. Ils sont imprimés avec des horodatages.
    sortinge les lignes en fonction de l’horodatage (le plus ancien en haut).
    head imprime les lignes supérieures, jusqu’aux 3 dernières lignes.
    cut supprime les horodatages.
    xargs exécute rm pour chaque fichier sélectionné.

    Pour 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 différents horodatages dans le dossier en cours. Exécutez ceci en premier et le code à supprimer pour tester le code.

    N’utilisez pas ls -t car il est dangereux pour les noms de fichiers pouvant contenir des espaces blancs 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 3 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 -- 

    Cela 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 tabulasortingce pour séparer les deux valeurs. sort divise l’entrée par la tabulasortingce et effectue un sorting numérique général qui sortinge correctement les nombres à virgule flottante. tail devrait être évidente et cut

    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, des noms de fichiers. Cette réponse utilise le caractère NULL.