Comment puis-je supprimer tous les fichiers sauf 10 les plus récents sous Linux?

49

J'essaie de garder un répertoire plein de fichiers journaux gérable. Tous les soirs, je veux supprimer tout sauf les 10 plus récents. Comment puis-je faire cela en une seule commande?

utilisateur75814
la source
3
jetez un oeil à logrotate
knittl
@ michael Comment la mienne peut-elle être une copie de celle-ci, si ma discussion a été créée en premier?
dev4life
@ovaherenow "le vôtre"? Je ne vois votre nom sur aucune des questions, alors je ne suis pas sûr de ce que vous voulez dire. Mon commentaire n'indique même pas lequel est un duplicata de l'autre. Mais ce n'est pas grave - c'est juste un commentaire, avec un lien. Il pourrait être ajouté à l'une ou l'autre des questions. C'est à quelqu'un d'autre - un administrateur - de marquer l'un ou l'autre comme duplicata. Aucune astuce néfaste n'était prévue et aucune ne devrait être perçue. La question qui a la meilleure réponse est plus importante encore que la question posée en premier. De nouveau, le lien facilite la discussion, il ne détermine pas la réponse.
michael
@ Michael Wow. C'est vraiment étrange. Ce fil a été ouvert par moi, mais ce n'est évidemment pas mon nom d'utilisateur. Mais je reçois une notification à propos de ce fil.
dev4life

Réponses:

58

Pour une solution portable et fiable, essayez ceci:

ls -1tr | head -n -10 | xargs -d '\n' rm -f --

La tail -n -10syntaxe de l'une des autres réponses ne semble pas fonctionner partout (c'est-à-dire pas sur mes systèmes RHEL5).

Et en utilisant $()ou ``sur la ligne de commande de rmcourt le risque de

  1. diviser les noms de fichiers avec des espaces, et
  2. dépassant la limite maximale de caractères en ligne de commande.

xargscorrige ces deux problèmes car il déterminera automatiquement le nombre d'arguments pouvant être passés dans la limite de caractères et -d '\n'ne divisera le résultat qu'à la limite de la ligne de l'entrée. Techniquement, cela peut toujours causer des problèmes aux noms de fichiers contenant une nouvelle ligne, mais c'est beaucoup moins courant que les noms de fichiers comportant des espaces, et le seul moyen de contourner les nouvelles lignes serait beaucoup plus compliqué, impliquant probablement au moins awk, sinon perl.

Si vous n'avez pas xargs (peut-être d'anciens systèmes AIX?), Vous pouvez en faire une boucle:

ls -1tr | head -n -10 | while IFS= read -r f; do
  rm -f "$f"
done

Cela sera un peu plus lent, car il génère un rmfichier distinct pour chaque fichier, tout en évitant les mises en garde 1 et 2 ci-dessus (mais souffre toujours de sauts de ligne dans les noms de fichiers).

Isaac Freeman
la source
@HaukeLaging: Extrait de la head(1)page de manuel:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman
Sous Solaris 10 head, xargsindiquez des erreurs. Les options correctes sont head -n 10et xargs rm -f. La commande complète estls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov
1
Notez que ls -1trle numéro UN n'est pas la lettre "L".
Utilisateur linux shonky
1
Il convient également de noter que les nouvelles lignes sont des caractères rares mais valides dans les noms de fichiers ext. Cela signifie que si vous avez les fichiers "that" et "that \ nthing", si ce script frappe "that \ nthing", il supprimera "that", une erreur s’il ne parvient pas à supprimer "thing", et laisse "that \ nthing" (la cible visée) inchangé.
Synthead
1
@IsaacFreeman J'ai supprimé mon mauvais conseil, bravo.
Walf
20

Le code que vous souhaitez inclure dans votre script est

 rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)

L' -1option (numérique) imprime chaque fichier sur une seule ligne, pour plus de sécurité. L' -foption à lui rmdit d'ignorer les fichiers inexistants pour quand lsne renvoie rien.

Daniel Böhmer
la source
Affiche originale, ici. J'ai essayé cette commande, mais cela ne fonctionne pas - je reçois le
message d'
2
Avez-vous utilisé le bon chemin? Évidemment, il /path/to/your/logsest juste fait que vous entriez le bon chemin. Pour trouver le bogue, exécutez les parties ls -t /path/...et ls -t /path/... | tail -n +11seul et vérifiez si le résultat est correct.
Daniel Böhmer le
1
@HaukeLaging Je suis sûr que tu as quelque chose qui cloche. Attention +au nombre avant. Cela tailne permet pas d’imprimer les n derniers éléments mais tous les éléments à partir du nième. Je me suis enregistré à nouveau et la commande ne devrait définitivement supprimer que les éléments plus anciens que les 10 fichiers les plus récents dans toutes les circonstances.
Daniel Böhmer
@halo En effet, désolé.
Hauke ​​Laging
2
Je pense que votre réponse est très dangereuse lsaffiche un nom de fichier, pas un chemin, alors vous rm -fallez essayer de supprimer les fichiers du répertoire actuel
Jürgen Steinblock
14

Apparemment, analyser lsest mauvais .

Si chaque fichier est créé quotidiennement et que vous souhaitez conserver les fichiers créés au cours des 10 derniers jours, vous pouvez le faire:

find /path/to/files -mtime 10 -delete

Ou si chaque fichier est créé arbitrairement:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
Celui
la source
5
-mtime 10 -deletesupprime uniquement les fichiers modifiés il y a exactement 10 jours. Les fichiers plus anciens ne seront pas supprimés. -mtime +11 -deletesupprime les fichiers modifiés il y a au moins 11 jours, laissant les 10 derniers jours des fichiers journaux.
Markus
1
Je pense que l'utilisation de %pau lieu de %Pest nécessaire sur le printfafin que les fichiers ont le chemin complet. Sinon le rm -rfne fonctionne pas.
Jalopaba
9

Un outil comme logrotate le fait pour vous. Cela facilite beaucoup la gestion des journaux. Vous pouvez également inclure des routines de nettoyage supplémentaires telles que suggérées par le halo.

BillThor
la source
2

J'ai modifié l'approche d'Isaac un peu.

Cela fonctionne maintenant avec le chemin souhaité:

ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
Sebastian U.
la source
1

À partir d' ici :

#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.


if [ $# -ne 2 ]; then
  echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
  exit 1
fi

cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
  ls -t | tail -n $files_to_delete | xargs rm
  if [ $? -ne 0 ]; then
    echo "An error ocurred deleting the files"
    exit 1
  else
    echo "$files_to_delete file(s) deleted."
  fi
else
  echo "nothing to delete!"
fi
manoflinux
la source
1
Merci d'avoir fourni cette réponse. Bien que fournir un code comme celui-ci soit utile, cela permet également de fournir des informations supplémentaires sur son utilisation ou son utilisation. Vous pouvez utiliser le bouton Modifier pour apporter des améliorations à votre message.
nhinkle
1

Dans Bash, vous pouvez essayer:

ls -1t | (i=0; while read f; do
  if [ $i -lt 10 ]; then
    ((i++))
    continue
  else
    rm -f "$f"
  fi
done)

Cela saute les 10 derniers als supprime le reste. logrotate est peut-être mieux, je veux juste corriger les mauvaises réponses liées au shell.

Hauke ​​Laging
la source
1

Je ne suis pas sûr que cela puisse aider qui que ce soit, mais pour éviter d'éventuels problèmes avec les caractères débiles, je viens d'utiliser lsle tri pour ensuite utiliser les fichiers inodepour le supprimer.

Pour le répertoire en cours, les 10 fichiers les plus récents sont conservés en fonction de l’heure de modification.

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;

Flo Woo
la source
0

J'avais besoin d'une solution élégante pour le busybox (routeur), toutes les solutions xargs ou array étaient inutiles pour moi - aucune commande de ce type n'y était disponible. trouver et mtime n'est pas la bonne réponse car nous parlons de 10 éléments et pas nécessairement de 10 jours. La réponse d'Espo était la plus courte et la plus propre et probablement la plus universelle.

Les erreurs d’espace et de suppression de fichiers sont toutes deux résolues simplement:

rm "$(ls -td *.tar | awk 'NR>7')" 2>&-

Version un peu plus éducative: Nous pouvons tout faire si nous utilisons awk différemment. Normalement, j'utilise cette méthode pour transmettre (renvoyer) les variables de l'awk au sh. Comme nous lisons tout le temps que cela ne peut pas être fait, je vous prie de différer: voici la méthode.

Exemple pour les fichiers .tar sans problème concernant les espaces dans le nom du fichier. Pour tester, remplacez "rm" par "ls".

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

Explication:

ls -td *.tarrépertorie tous les fichiers .tar triés par heure. Pour appliquer à tous les fichiers du dossier actuel, supprimez la partie "d * .tar".

awk 'NR>7... saute les 7 premières lignes

print "rm \"" $0 "\"" construit une ligne: rm "nom de fichier"

eval l'exécute

Puisque nous utilisons rm, je n’utiliserais pas la commande ci-dessus dans un script! Une utilisation plus judicieuse est:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

Dans le cas d'utilisation de ls -tcommande ne fera aucun mal sur des exemples aussi stupides que: touch 'foo " bar'et touch 'hello * world'. Non pas que nous créons jamais des fichiers avec de tels noms dans la vie réelle!

Sidenote. Si nous voulions transmettre une variable au sh de cette façon, nous modifierions simplement l'impression:

print "VarName="$1

pour définir la variable VarNamesur la valeur de $1. Plusieurs variables peuvent être créées en une fois. Cela VarNamedevient une variable sh normale et peut ensuite être utilisé dans un script ou un shell. Donc, pour créer des variables avec awk et les restituer au shell:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"
Pila
la source
0

Je conviens que l'analyse de ls est diabolique!

Assurez-vous que seuls les 5 derniers fichiers sont conservés dans le /srv/backupschemin.

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r
h0tw1r3
la source
0

Basé sur la réponse d'Isaac Freeman, mais travaillant sur n'importe quel répertoire:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

Vous pouvez également définir un préfixe, comme $PATH_TO_DELETE/testFi*

Si vous utilisez *après la PATH lssortie des noms de fichiers absolus, au moins dans "mon" bash :)

Bis
la source