Löschen Sie alle, aber die jüngsten X-Dateien in bash
Frage
Gibt es eine einfache Möglichkeit, in eine ziemlich standard-UNIX-Umgebung mit der bash, um einen Befehl auszuführen, um alle löschen, aber den letzten X Dateien aus einem Verzeichnis?
Zu geben, ein bisschen mehr von ein konkretes Beispiel, stellen Sie sich einige cron-job schreiben aus einer Datei (z.B. eine log-Datei oder ein tar-ed up backup) auf ein Verzeichnis jeder Stunde.Ich möchte eine Möglichkeit haben, einen anderen cron-job laufen, der die würde entfernen Sie die ältesten Dateien in dem Verzeichnis, bis es weniger als, sagen wir, 5.
Und nur um klar zu sein, es ist nur eine Datei vorhanden, es sollte nie gelöscht werden.
Lösung
Die Probleme mit den vorhandenen Antworten:
- Unfähigkeit, damit umzugehen Dateinamen mit eingebetteten Leerzeichen oder Zeilenumbrüche.
- im Fall von Lösungen, die von invoke
rm
direkt an einem nicht börsennotierten Substitutionen (rm `...`
), besteht die zusätzliche Gefahr der unbeabsichtigten Platzhalter.
- im Fall von Lösungen, die von invoke
- Unfähigkeit zu unterscheiden zwischen Dateien und Verzeichnissen (D. H., wenn Verzeichnisse geschehen zu werden zwischen der 5 zuletzt geänderten Dateisystem-Positionen, würden Sie effektiv behalten weniger als 5-Dateien und die Anwendung
rm
auf Verzeichnisse fehlschlagen).
wnoise Antwort diese Fragen behandelt werden, aber die Lösung ist GNU-Besondere (und ziemlich komplexen).
Hier ist eine pragmatische, POSIX-konforme Lösung , dass kommt mit nur ein Nachteil:es nicht in den Griff Dateinamen mit eingebetteten Zeilenumbrüche - aber ich weiß nicht berücksichtigen, dass eine Reale Sorge für die meisten Menschen.
Für die Aufzeichnung, hier ist die Erklärung dafür, warum es im Allgemeinen keine gute Idee, zu analysieren ls
Ausgabe: http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
Die oben ist ineffizient, weil xargs
hat aufrufen rm
einmal für jeder mit dem Namen.
Ihre Plattform xargs
möglicherweise können Sie dieses problem lösen:
Wenn Sie haben GNU xargs
, verwenden Sie -d '\n'
, wodurch xargs
betrachten Sie jeden Eingang eine separate argument, doch geht Sie so viele Argumente wie passen die in der Befehlszeile auf einmal:
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
(--no-run-if-empty
) wird sichergestellt, dass rm
wird nicht aufgerufen, wenn es keine Eingabe.
Wenn Sie haben BSD xargs
(einschließlich OS X), die Sie verwenden können, -0
zu handhaben NUL
getrennte Eingabe, nach der ersten übersetzung von Zeilenumbrüchen zu NUL
(0x0
) chars., was auch geht (in der Regel) alle Dateinamen auf einmal (funktioniert auch mit GNU xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
Erklärung:
ls -tp
druckt den Namen des Dateisystems, Inhalte nach wie vor kurzem geändert wurden , in absteigender Reihenfolge (zuletzt geänderten Artikel zuerst) (-t
), mit Verzeichnissen gedruckt mit einem trailing/
um diese als solche markieren (-p
).grep -v '/$'
dann werden Sie das Unkraut Verzeichnisse aus der resultierenden Liste durch das auslassen (-v
) Linien, die einen trailing/
(/$
).- Caveat:Da ein symlink, der auf ein Verzeichnis zeigt technisch nicht selbst ein Verzeichnis, eine solche symlinks werden nicht ausgeschlossen werden.
tail -n +6
überspringt die ersten 5 Einträge in der Liste im Effekt Rückkehr alle aber die 5 zuletzt geänderten Dateien, falls vorhanden.
Beachten Sie, dass in Auftrag zu ausschließenN
Dateien,N+1
übergeben werden müssentail -n +
.xargs -I {} rm -- {}
(und seine Variationen), dann ruft anrm
auf alle diese Dateien;wenn keine übereinstimmungen vorhanden sind, an alle,xargs
nichts.xargs -I {} rm -- {}
definiert Platzhalter{}
das stellt jeden Eingang insgesamt, sorm
ist es dann aufgerufen, einmal für jede Zeile, aber mit Dateinamen mit Leerzeichen korrekt behandelt.--
in allen Fällen sichergestellt, dass alle Dateinamen, die passieren, mit zu beginnen-
nicht verwechselt Optionen vonrm
.
Ein variation auf das ursprüngliche problem, in Fall die passenden Dateien die verarbeitet werden müssen individuell oder gesammelt in einem shell-array:
# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done
# One by one, but using a Bash process substitution (<(...),
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)
# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
Andere Tipps
Entfernen Sie alle, aber 5 (oder was auch immer Anzahl) der letzten Dateien in einem Verzeichnis.
rm `ls -t | awk 'NR>5'`
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm
Diese version unterstützt die Namen mit Leerzeichen:
(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
Eine einfachere Variante von thelsdj Antwort:
ls -tr | head -n -5 | xargs --no-run-if-empty rm
ls -tr zeigt alle Dateien, älteste zuerst (-t neuste zuerst-r reverse).
head-n -5 zeigt alle aber die letzten 5 Zeilen (also die 5 neuesten Dateien).
xargs rm Aufrufe rm für jede ausgewählte Datei.
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f
Erfordert GNU-für -printf und GNU-Sortieren nach -z von GNU-awk für das "\0", und GNU xargs für -0, sondern behandelt-Dateien mit eingebetteten Zeilenumbrüche oder Leerzeichen.
All diese Antworten ausfallen, wenn es Verzeichnisse im aktuellen Verzeichnis.Hier ist etwas, das funktioniert:
find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm
Diese:
funktioniert, wenn es Verzeichnisse im aktuellen Verzeichnis
versucht zu entfernen, wird jede Datei, auch wenn die vorangegangene konnte nicht entfernt werden (aufgrund von Berechtigungen, etc.)
fehl sicher, wenn die Anzahl der Dateien im aktuellen Verzeichnis wird eine übermäßige und
xargs
normalerweise Schraube, die Sie über (die-x
)nicht gerecht werden Leerzeichen in Dateinamen (vielleicht bist du mit dem falschen Betriebssystem?)
ls -tQ | tail -n+4 | xargs rm
Liste von Dateinamen, die durch Veränderung der Zeit, die Angabe jeweils mit dem Namen.Ausschließen ersten 3 Absatz 3 die letzten).Entfernen Sie die verbleibenden.
BEARBEITEN nach hilfreichen Kommentar von mklement0 (danke!):behoben -n+3-argument, und beachten Sie, dass dies nicht wie erwartet funktioniert, wenn Dateinamen Zeilenumbrüche enthalten und/oder das Verzeichnis enthält Unterverzeichnisse.
Ignoriert Zeilenumbrüche ignoriert die Sicherheit und eine gute Codierung.wnoise hatte die einzige gute Antwort.Hier ist eine variation auf dem, legt die Dateinamen in einem array $x
while IFS= read -rd ''; do
x+=("${REPLY#* }");
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
Wenn die Dateinamen, die nicht über Räume, das arbeiten:
ls -C1 -t| awk 'NR>5'|xargs rm
Wenn die Dateinamen haben Räume, so etwas wie
ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh
Basic logic:
- Holen Sie sich eine Liste der Dateien, die in der Zeit, um eine Säule, die
- Holen Sie sich alle, aber die ersten 5 (n=5 für dieses Beispiel)
- erste version:senden Sie diese an rm
- zweite version:gen eine Skript, dass wird entfernen Sie Sie richtig
Mit zsh
Vorausgesetzt, Sie kümmern sich nicht um Verzeichnisse vorhanden und Sie werden nicht mehr als 999 Dateien (wählen Sie eine größer Nummer, wenn Sie möchten, oder erstellen Sie eine while-Schleife).
[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])
In *(.om[6,999])
, die .
bedeutet, dass die Dateien, die o
bedeutet Sortierreihenfolge, bis die m
Mittel, mit Datum der änderung (put a
für den Zugang Zeit-oder c
für die inode-änderung), die [6,999]
wählt einen Bereich der Datei, so dass Sie nicht rm die 5 ersten.
Ich weiß, dies ist ein Alter thread, aber vielleicht wird jemand davon profitieren.Mit diesem Befehl werden die Dateien zu finden, in das aktuelle Verzeichnis :
for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done
Dies ist ein wenig robuster als einige der vorherigen Antworten, wie es erlaubt, zu begrenzen Ihre such-domain, um passende Dateien Ausdrücken.Suchen Sie zunächst Dateien mit was auch immer die Bedingungen, die Sie wollen.Drucken Sie diese Dateien mit dem Zeitstempel neben Ihnen.
find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'
Als Nächstes Sortieren Sie durch die timestamps:
sort -r -z -n
Dann hauen die 4 neuesten Dateien aus der Liste:
tail -n+5
Schnappen Sie sich die 2. Spalte (die mit dem Namen, nicht den timestamp):
awk '{ print $2; }'
Und dann wickeln Sie das ganze in eine for-Anweisung:
for F in $(); do rm $F; done
Dies kann ein Ausführlicher Befehl, aber ich hatte viel mehr Glück als in der Lage zu Ziel die bedingte Dateien und führen komplexere Befehle gegen Sie.
interessant fand Sie " cmd " in das Sed-Onliners - Löschen von letzten 3 Zeilen - fnd es perfekt für einen anderen Weg, um die Haut der Katze (OK das nicht) aber die Idee:
#!/bin/bash
# sed cmd chng #2 to value file wish to retain
cd /opt/depot
ls -1 MyMintFiles*.zip > BigList
sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList
for i in `cat DeList`
do
echo "Deleted $i"
rm -f $i
#echo "File(s) gonzo "
#read junk
done
exit 0
Entfernt alle aber die letzten 10 (most recents) - Dateien
ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm
Wenn weniger als 10 Dateien, keine Datei wird entfernt, und Sie haben :error Kopf:rechtswidrige line count -- 0
Ich brauchte eine elegante Lösung für die busybox (router), alle xargs-oder array-Lösungen waren für mich nutzlos - kein derartiger Befehl vorhanden.find und mtime ist nicht die richtige Antwort, wie wir reden über 10 Artikel und nicht unbedingt 10 Tage.Espo wurde die Antwort der kürzesten und saubersten und wahrscheinlich die Allgemeine ein.
Fehler mit Leerzeichen und wenn keine Dateien gelöscht werden, sind sowohl einfach gelöst, der standard-Weg:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
Etwas mehr Bildungs-version:Wir können tun es alle, wenn wir die awk anders.Normalerweise verwende ich diese Methode zu übergeben (Rückgabe -) Variablen von awk, um die sh.Wie Lesen wir die ganze Zeit nicht getan werden kann, ich bitte zu unterscheiden:hier ist die Methode.
Beispiel für .tar-Dateien ohne problem in Bezug auf die Leerzeichen im Dateinamen.Um zu testen, ersetzen Sie "rm" mit dem "ls".
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
Erklärung:
ls -td *.tar
listet alle .tar-Dateien, sortiert nach der Zeit.Für alle Dateien im aktuellen Ordner entfernen, die "d *.tar" Teil
awk 'NR>7...
überspringt die ersten 7 Zeilen
print "rm \"" $0 "\""
konstruiert eine Linie:rm "file name"
eval
führt es
Da sind wir mit rm
, Ich würde nicht verwenden Sie den obigen Befehl in einem Skript!Weiser Verwendung ist:
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
Im Falle der Verwendung ls -t
Befehl wird nicht Schaden, auf solche dummen Beispiele: touch 'foo " bar'
und touch 'hello * world'
.Nicht, dass wir jemals erstellen, die Dateien mit solchen Namen im wirklichen Leben!
Sidenote.Wenn wir wollten, übergeben Sie eine variable an die sh auf diese Weise würden wir einfach nur ändern die drucken (einfache form, keine Leerzeichen toleriert):
print "VarName="$1
die variable VarName
der Wert von $1
.Mehrere Variablen können erstellt werden in einem gehen.Diese VarName
wird zu einem normalen sh variabel und kann normalerweise verwendet werden, die in einem Skript oder die shell danach.So, zum erstellen von Variablen mit awk und geben Sie zurück an die shell:
eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\"" }'); echo "$VarName"
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))
# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0
ls -t *.log | tail -$tailCount | xargs rm -f
Ich machte dies in einer bash-shell-Skript.Verwendung: keep NUM DIR
wobei NUM die Anzahl der Dateien, zu halten und zu DIR ist das Verzeichnis, zu schrubben.
#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
echo "Usage: $0 NUMFILES DIR"
echo "Keep last N newest files."
exit 1
fi
if [ ! -e $2 ]; then
echo "ERROR: directory '$1' does not exist"
exit 1
fi
if [ ! -d $2 ]; then
echo "ERROR: '$1' is not a directory"
exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l
Unter Debian (vorausgesetzt, es ist die gleiche auf der anderen Distributionen, die ich bekomme:rm:kann nicht entfernen Sie directory `..'
das ist ziemlich nervig..
Ja, ich Zwickte die oben und auch Hinzugefügt grep mit dem Befehl.In meinem Fall habe ich 6 backup-Dateien in einem Verzeichnis z.B.Datei1.tar-Datei2.tar-datei3.tar etc, und ich will nur das löschen der ältesten Datei (entfernen Sie die erste Datei in meinem Fall)
Das Skript lief ich zum löschen die ältesten Datei war:
ls -K1-t| grep-Datei | awk 'NR>5'|xargs rm
Dieses (wie oben) löscht den ersten der meine Dateien z.B.Datei1.tar auch diese Blätter werden mit Datei2 datei3 datei4 file5 und file6