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?
@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
Teilen von Dateinamen mit Leerzeichen und
Ü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).
@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.
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
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:
-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.
#! /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
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.
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.
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".
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:
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:
Antworten:
Versuchen Sie Folgendes, um eine tragbare und zuverlässige Lösung zu erhalten:
Die
tail -n -10
Syntax in einer der anderen Antworten scheint nicht überall zu funktionieren (dh nicht auf meinen RHEL5-Systemen).Und mit
$()
oder``
auf der Kommandozeile vonrm
läuft das Risiko vonxargs
behebt 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:
Dies ist etwas langsamer, da
rm
fü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).quelle
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
head
undxargs
Fehler geben. Richtige Optionen sindhead -n 10
undxargs rm -f
. Voller Befehl istls -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
es sich bei der Nummer EINS nicht um den Buchstaben "L" handelt.Der Code, den Sie in Ihr Skript aufnehmen möchten, lautet
Die
-1
(numerische) Option druckt aus Sicherheitsgründen jede Datei in einer einzelnen Zeile. Die-f
Option, dierm
angibt, dass nicht vorhandene Dateien ignoriert werden sollen, wennls
nichts zurückgegeben wird.quelle
/path/to/your/logs
nur den richtigen Pfad eingeben. Um die Fehler der Teile ausführenls -t /path/...
undls -t /path/... | tail -n +11
allein und überprüfen , ob das Ergebnis korrekt ist.+
vor der Nummer. Es werdentail
nicht 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.ls
Drucke einen Dateinamen, keinen Pfad, also wirstrm -f
du versuchen, die Dateien im aktuellen Verzeichnis zu entfernenAnscheinend ist Parsen
ls
bö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:
Oder wenn jede Datei willkürlich erstellt wird:
quelle
-mtime 10 -delete
löscht nur Dateien, die vor genau 10 Tagen geändert wurden. Ältere Dateien werden nicht gelöscht.-mtime +11 -delete
löscht Dateien, die vor 11 oder mehr Tagen geändert wurden, und belässt die letzten 10 Tage der Protokolldateien.%p
anstelle von%P
wird auf dem benötigt,printf
damit die Dateien den vollen Pfad haben. Sonst klappt dasrm -rf
nicht.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.
quelle
Ich habe Isaacs Herangehensweise ein wenig modifiziert .
Es funktioniert jetzt mit einem gewünschten Pfad:
quelle
Von hier :
quelle
In Bash können Sie versuchen:
Dies überspringt die 10 neuesten und löscht den Rest. logrotate kann besser sein, ich möchte nur die falschen Shell-bezogenen Antworten korrigieren.
quelle
Ich bin mir nicht sicher, ob dies jemandem helfen wird, aber um mögliche Probleme mit Wonky-Zeichen zu vermeiden, habe ich nur
ls
die Sortierung durchgeführt, aber dann die Dateieninode
zum 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 {} \;
quelle
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:
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".
Erläuterung:
ls -td *.tar
listet alle .tar-Dateien nach der Zeit sortiert auf. Entfernen Sie den Teil "d * .tar", um ihn auf alle Dateien im aktuellen Ordner anzuwendenawk 'NR>7...
überspringt die ersten 7 Zeilenprint "rm \"" $0 "\""
erstellt eine Zeile: rm "Dateiname"eval
führt es ausDa wir verwenden
rm
, würde ich den obigen Befehl nicht in einem Skript verwenden! Klügerer Gebrauch ist:Im Falle der Verwendung von
ls -t
befehlen werden solche albernen Beispiele wie:touch 'foo " bar'
und keinen Schaden anrichtentouch '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:
um die Variable
VarName
auf den Wert von zu setzen$1
. Es können mehrere Variablen auf einmal erstellt werden. DiesVarName
wird 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:quelle
Ich stimme zu, dass das Parsen von ls böse ist!
Stellen Sie sicher, dass sich nur die letzten 5 Dateien im
/srv/backups
Pfad befinden.quelle
Basierend auf der Antwort von Isaac Freeman, aber in jedem Verzeichnis:
Sie können auch ein Präfix festlegen, z
$PATH_TO_DELETE/testFi*
Wenn Sie
*
nach dem verwenden,PATH
ls
werden absolute Dateinamen ausgegeben, zumindest in "my" bash :)quelle