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.

War es hilfreich?

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.
  • 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 NULgetrennte 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ßen N Dateien, N+1 übergeben werden müssen tail -n +.
  • xargs -I {} rm -- {} (und seine Variationen), dann ruft an rm auf alle diese Dateien;wenn keine übereinstimmungen vorhanden sind, an alle, xargs nichts.
    • xargs -I {} rm -- {} definiert Platzhalter {} das stellt jeden Eingang insgesamt, so rm 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 von rm.

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:

  1. funktioniert, wenn es Verzeichnisse im aktuellen Verzeichnis

  2. versucht zu entfernen, wird jede Datei, auch wenn die vorangegangene konnte nicht entfernt werden (aufgrund von Berechtigungen, etc.)

  3. fehl sicher, wenn die Anzahl der Dateien im aktuellen Verzeichnis wird eine übermäßige und xargs normalerweise Schraube, die Sie über (die -x)

  4. 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

Zu zählen Dateien mit bash

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top