Wie lösche ich alle bis auf 10 neuesten Dateien in Linux?

49

Ich versuche, ein Verzeichnis voller Protokolldateien übersichtlich zu halten. Nächtlich möchte ich alle bis auf die letzten 10 löschen. Wie kann ich das in einem einzigen Befehl tun?

user75814
quelle
3
Werfen
Sie
@michael Wie kann meins ein Duplikat davon sein, wenn meine Diskussion zuerst erstellt wurde?
Dev4life
@ovaherenow "deins"? Ich sehe Ihren Namen in keiner der beiden Fragen, daher bin ich mir nicht sicher, was Sie meinen. Mein Kommentar gibt nicht einmal an, welches ein Duplikat des anderen ist. Aber es spielt keine Rolle - es ist nur ein Kommentar mit einem Link. Es kann entweder zu einer Frage oder zu beiden Fragen hinzugefügt werden. Es liegt an jemand anderem - einem Administrator -, das eine oder andere als Duplikat zu markieren. Es war kein ruchloser Kampf beabsichtigt und sollte auch nicht wahrgenommen werden. Wichtiger als die Frage, die zuerst gestellt wurde, ist die Frage, auf die die beste Antwort gefunden wurde.
Michael
@ Michael Wow. Das ist wirklich seltsam. Dieser Thread wurde von mir geöffnet, aber das ist offensichtlich nicht mein Benutzername. Aber ich werde über diesen Thread benachrichtigt.
dev4life

Antworten:

58

Versuchen Sie Folgendes, um eine tragbare und zuverlässige Lösung zu erhalten:

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

Die tail -n -10Syntax in einer der anderen Antworten scheint nicht überall zu funktionieren (dh nicht auf meinen RHEL5-Systemen).

Und mit $()oder ``auf der Kommandozeile von rmläuft das Risiko von

  1. Teilen von Dateinamen mit Leerzeichen und
  2. Überschreitung der maximalen Anzahl von Kommandozeilenzeichen.

xargsbehebt diese beiden Probleme, da automatisch ermittelt wird, wie viele Argumente innerhalb der Zeichenbegrenzung übergeben werden können, und mit dem -d '\n'wird nur an der Zeilengrenze der Eingabe geteilt. Technisch kann dies immer noch Probleme für Dateinamen mit einem Zeilenumbruch verursachen, aber das ist weitaus weniger verbreitet als bei Dateinamen mit Leerzeichen, und der einzige Weg, um die Zeilenumbrüche zu umgehen, wäre viel komplizierter, wahrscheinlich mit mindestens awk, wenn nicht mit Perl.

Wenn Sie keine xargs haben (alte AIX-Systeme vielleicht?), Könnten Sie eine Schleife daraus machen:

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

Dies ist etwas langsamer, da rmfür jede Datei eine eigene Datei erstellt wird. Die obigen Einschränkungen 1 und 2 werden jedoch vermieden (es treten jedoch weiterhin Zeilenumbrüche in den Dateinamen auf).

Isaac Freeman
quelle
@HaukeLaging: Aus der head(1)Manpage:-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
Auf Solaris 10 headund xargsFehler geben. Richtige Optionen sind head -n 10und xargs rm -f. Voller Befehl istls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov
1
Beachten Sie, dass ls -1tres sich bei der Nummer EINS nicht um den Buchstaben "L" handelt.
Shonky Linux Benutzer
1
Es sollte auch beachtet werden, dass Zeilenumbrüche seltene, aber gültige Zeichen in ext-Dateinamen sind. Dies bedeutet, dass, wenn Sie die Dateien "that" und "that \ nthing" haben und dieses Skript "that \ nthing" trifft, "that" gelöscht wird, ein Fehler auftritt, wenn "thing" nicht gelöscht werden konnte, und verlassen wird "that \ nthing" (das beabsichtigte Ziel) bleibt davon unberührt.
Synthead
1
@IsaacFreeman Ich habe meinen schlechten Rat gelöscht, Prost.
Walf
20

Der Code, den Sie in Ihr Skript aufnehmen möchten, lautet

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

Die -1(numerische) Option druckt aus Sicherheitsgründen jede Datei in einer einzelnen Zeile. Die -fOption, die rmangibt, dass nicht vorhandene Dateien ignoriert werden sollen, wenn lsnichts zurückgegeben wird.

Daniel Böhmer
quelle
Originalplakat hier. Ich habe diesen Befehl ausprobiert, aber er funktioniert nicht - ich erhalte die Fehlermeldung "rm: missing operand"
user75814
2
Hast du den richtigen Pfad benutzt? Offensichtlich müssen Sie /path/to/your/logsnur den richtigen Pfad eingeben. Um die Fehler der Teile ausführen ls -t /path/...und ls -t /path/... | tail -n +11allein und überprüfen , ob das Ergebnis korrekt ist.
Daniel Böhmer
1
@HaukeLaging Ich bin mir ziemlich sicher, dass du etwas falsch gemacht hast. Beachten Sie die +vor der Nummer. Es werden tailnicht die letzten n Elemente gedruckt, sondern alle Elemente, die ab dem n-ten beginnen. Ich habe erneut eingecheckt und der Befehl sollte auf jeden Fall nur Elemente entfernen, die älter als 10 der neuesten Dateien sind.
Daniel Böhmer
@halo In der Tat, sorry.
Hauke ​​Laging
2
Ich denke, deine Antwort ist sehr gefährlich. lsDrucke einen Dateinamen, keinen Pfad, also wirst rm -fdu versuchen, die Dateien im aktuellen Verzeichnis zu entfernen
Jürgen Steinblock
14

Anscheinend ist Parsen lsböse .

Wenn jede Datei täglich erstellt wird und Sie Dateien behalten möchten, die innerhalb der letzten 10 Tage erstellt wurden, haben Sie folgende Möglichkeiten:

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

Oder wenn jede Datei willkürlich erstellt wird:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
Der Eine
quelle
5
-mtime 10 -deletelöscht nur Dateien, die vor genau 10 Tagen geändert wurden. Ältere Dateien werden nicht gelöscht. -mtime +11 -deletelöscht Dateien, die vor 11 oder mehr Tagen geändert wurden, und belässt die letzten 10 Tage der Protokolldateien.
Markus
1
Ich denke die Verwendung von %panstelle von %Pwird auf dem benötigt, printfdamit die Dateien den vollen Pfad haben. Sonst klappt das rm -rfnicht.
Jalopaba
9

Ein Tool wie Logrotate erledigt dies für Sie. Dies erleichtert die Protokollverwaltung erheblich. Sie können auch zusätzliche Bereinigungsroutinen einschließen, die als Halo vorgeschlagen werden.

BillThor
quelle
2

Ich habe Isaacs Herangehensweise ein wenig modifiziert .

Es funktioniert jetzt mit einem gewünschten Pfad:

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

Von hier :

#! /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
quelle
1
Vielen Dank für diese Antwort. Obwohl das Bereitstellen von Code wie diesem hilfreich ist, ist es auch hilfreich, einige zusätzliche Informationen darüber bereitzustellen, wie er möglicherweise verwendet wird oder was der Code bewirkt. Mit der Schaltfläche " Bearbeiten" können Sie zukünftige Verbesserungen an Ihrem Beitrag vornehmen.
Nhinkle
1

In Bash können Sie versuchen:

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

Dies überspringt die 10 neuesten und löscht den Rest. logrotate kann besser sein, ich möchte nur die falschen Shell-bezogenen Antworten korrigieren.

Hauke ​​Laging
quelle
1

Ich bin mir nicht sicher, ob dies jemandem helfen wird, aber um mögliche Probleme mit Wonky-Zeichen zu vermeiden, habe ich nur lsdie Sortierung durchgeführt, aber dann die Dateien inodezum Löschen verwendet.

Für das aktuelle Verzeichnis werden die letzten 10 Dateien basierend auf der Änderungszeit gespeichert.

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

Flo Woo
quelle
0

Ich brauchte eine elegante Lösung für die Busybox (Router), alle xargs- oder Array-Lösungen waren für mich nutzlos - ein solcher Befehl war dort nicht verfügbar. find and mtime ist nicht die richtige Antwort, da es sich um 10 Artikel handelt und nicht unbedingt um 10 Tage. Espos Antwort war die kürzeste und sauberste und wahrscheinlich die unversalste.

Fehler mit Leerzeichen und wenn keine Dateien gelöscht werden sollen, werden beide einfach auf die übliche Weise behoben:

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

Etwas lehrreichere Version: Wir können alles machen, wenn wir awk anders benutzen. Normalerweise benutze ich diese Methode, um Variablen von awk an sh zu übergeben (zurückzugeben). Da wir die ganze Zeit lesen, was nicht möglich ist, möchte ich unterscheiden: Hier ist die Methode.

Beispiel für .tar-Dateien ohne Probleme bezüglich der Leerzeichen im Dateinamen. Ersetzen Sie zum Testen "rm" durch "ls".

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

Erläuterung:

ls -td *.tarlistet alle .tar-Dateien nach der Zeit sortiert auf. Entfernen Sie den Teil "d * .tar", um ihn auf alle Dateien im aktuellen Ordner anzuwenden

awk 'NR>7... überspringt die ersten 7 Zeilen

print "rm \"" $0 "\"" erstellt eine Zeile: rm "Dateiname"

eval führt es aus

Da wir verwenden rm, würde ich den obigen Befehl nicht in einem Skript verwenden! Klügerer Gebrauch ist:

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

Im Falle der Verwendung von ls -tbefehlen werden solche albernen Beispiele wie: touch 'foo " bar'und keinen Schaden anrichten touch 'hello * world'. Nicht, dass wir jemals Dateien mit solchen Namen im wirklichen Leben erstellen würden!

Randnotiz. Wenn wir auf diese Weise eine Variable an sh übergeben möchten, ändern wir einfach den Ausdruck:

print "VarName="$1

um die Variable VarNameauf den Wert von zu setzen $1. Es können mehrere Variablen auf einmal erstellt werden. Dies VarNamewird eine normale sh-Variable und kann danach normalerweise in einem Skript oder einer Shell verwendet werden. So erstellen Sie Variablen mit awk und geben sie an die Shell zurück:

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

Ich stimme zu, dass das Parsen von ls böse ist!

Stellen Sie sicher, dass sich nur die letzten 5 Dateien im /srv/backupsPfad befinden.

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

Basierend auf der Antwort von Isaac Freeman, aber in jedem Verzeichnis:

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

Sie können auch ein Präfix festlegen, z $PATH_TO_DELETE/testFi*

Wenn Sie *nach dem verwenden, PATH lswerden absolute Dateinamen ausgegeben, zumindest in "my" bash :)

Bis
quelle