Die Probleme mit den vorhandenen Antworten:
- Unfähigkeit, Dateinamen mit eingebetteten Leerzeichen oder Zeilenumbrüchen zu behandeln.
- Bei Lösungen, die
rm
direkt bei einer nicht zitierten Befehlssubstitution ( rm `...`
) aufgerufen werden , besteht ein zusätzliches Risiko für unbeabsichtigtes Globbing.
- Unfähigkeit, zwischen Dateien und Verzeichnissen zu unterscheiden (dh wenn Verzeichnisse zu den 5 zuletzt geänderten Dateisystemelementen gehören, behalten Sie effektiv weniger als 5 Dateien bei, und die Anwendung
rm
auf Verzeichnisse schlägt fehl).
Die Antwort von wnoise befasst sich mit diesen Problemen, aber die Lösung ist GNU- spezifisch (und recht komplex).
Hier ist eine pragmatische, POSIX-kompatible Lösung , die nur eine Einschränkung aufweist : Sie kann keine Dateinamen mit eingebetteten Zeilenumbrüchen verarbeiten - aber ich halte dies für die meisten Menschen nicht für ein echtes Problem .
Hier ist die Erklärung, warum es im Allgemeinen keine gute Idee ist, die ls
Ausgabe zu analysieren : http://mywiki.wooledge.org/ParsingLs
ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}
Die oben ist ineffizient , weil xargs
aufgerufen hat rm
einmal für jeden Dateinamen.
Mit Ihrer Plattform xargs
können Sie möglicherweise dieses Problem lösen:
Wenn Sie GNU xargs
, Verwendung -d '\n'
, die Marken xargs
jede Eingabezeile ein separates Argument betrachten, geht noch so viele Argumente wie in einer Befehlszeile passen auf einmal :
ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --
-r
( --no-run-if-empty
) stellt sicher, dass dies rm
nicht aufgerufen wird, wenn keine Eingabe erfolgt.
Wenn Sie BSD xargs
(einschließlich auf macOS ), können Sie -0
behandeln zu NUL
-separated Eingang, nach dem ersten übersetzen Zeilenumbrüchen NUL
( 0x0
) Zeichen, die auch passiert ( in der Regel) alle Dateinamen. Sofort (funktioniert auch mit GNU xargs
):
ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --
Erläuterung:
ls -tp
druckt die Namen von Dateisystemelementen sortiert nach der letzten Änderung in absteigender Reihenfolge (zuletzt zuerst geänderte Elemente) ( -t
), wobei Verzeichnisse mit einem nachgestellten /
Zeichen gedruckt werden , um sie als solche zu markieren ( -p
).
grep -v '/$'
Anschließend werden Verzeichnisse aus der resultierenden Liste entfernt, indem ( -v
) Zeilen mit einem nachgestellten /
( /$
) weggelassen werden .
- Vorsichtsmaßnahme : Da ein Symlink, der auf ein Verzeichnis verweist, technisch gesehen selbst kein Verzeichnis ist, werden solche Symlinks nicht ausgeschlossen.
tail -n +6
Überspringt die ersten 5 Einträge in der Liste und gibt alle bis auf die 5 zuletzt geänderten Dateien zurück, falls vorhanden.
Beachten Sie, dass zum Ausschließen von N
Dateien an übergeben werden N+1
muss tail -n +
.
xargs -I {} rm -- {}
(und seine Variationen) ruft dann rm
alle diese Dateien auf; Wenn es überhaupt keine Übereinstimmungen gibt, xargs
wird nichts getan.
xargs -I {} rm -- {}
Definiert einen Platzhalter {}
, der jede Eingabezeile als Ganzes darstellt . Er rm
wird dann einmal für jede Eingabezeile aufgerufen, wobei Dateinamen mit eingebetteten Leerzeichen korrekt behandelt werden.
--
In jedem Fall wird sichergestellt, dass Dateinamen, mit denen zufällig begonnen -
wird, nicht mit Optionen von verwechselt werden rm
.
Eine Variation auf dem ursprünglichen Problem, im Fall , dass die passenden Dateien verarbeitet werden einzeln oder in einer Schale gesammelt 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
ls
nicht im aktuellen Verzeichnis befinden, enthalten die Pfade zu Dateien '/', was bedeutet, dassgrep -v '/'
nichts übereinstimmt. Ich glaube,grep -v '/$'
Sie möchten nur Verzeichnisse ausschließen.grep
Befehl auch konzeptionell klarer wird. Beachten Sie jedoch, dass das von Ihnen beschriebene Problem nicht mit einem einzigen Verzeichnispfad aufgetreten wäre . zBls -p /private/var
würde immer noch nur bloße Dateinamen drucken. Nur wenn Sie mehrere Dateiargumente übergeben haben (normalerweise über einen Glob), werden die tatsächlichen Pfade in der Ausgabe angezeigt. zBls -p /private/var/*
(und Sie würden auch den Inhalt übereinstimmender Unterverzeichnisse sehen, es sei denn, Sie haben auch eingeschlossen-d
).Entfernen Sie alle bis auf 5 (oder eine beliebige Anzahl) der neuesten Dateien in einem Verzeichnis.
quelle
ls -t
zuls -td *.bz2
ls -t | awk 'NR>1'
(ich wollte nur das neueste). Vielen Dank!ls -t | awk 'NR>5' | xargs rm -f
Wenn Sie Pipes bevorzugen und den Fehler unterdrücken müssen, wenn nichts gelöscht werden soll.touch 'hello * world'
löschen, wird absolut alles im aktuellen Verzeichnis gelöscht .Diese Version unterstützt Namen mit Leerzeichen:
quelle
(ls -t|head -n 5;ls)
ist eine Befehlsgruppe . Die 5 neuesten Dateien werden zweimal gedruckt.sort
fügt identische Zeilen zusammen.uniq -u
Entfernt Duplikate, sodass alle bis auf die 5 neuesten Dateien erhalten bleiben.xargs rm
ruftrm
jeden von ihnen an.--no-run-if-empty
zuxargs
wie in(ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rm
bitte die Antwort aktualisieren.touch 'foo " bar'
Der gesamte Rest des Befehls wird entfernt.xargs -d $'\n'
als einzuspritzen Zitate in Ihre Inhalte, wenn die Eingangsstrom NUL-abgrenzt (die als die Verwendung von etwas anderes erfordert ,ls
um wirklich richtig tun) ist die ideale Wahl.Einfachere Variante der Antwort von thelsdj:
ls -tr zeigt alle Dateien an, die älteste zuerst (-t die neueste zuerst, -r umgekehrt).
head -n -5 zeigt alle bis auf die 5 letzten Zeilen an (dh die 5 neuesten Dateien).
xargs rm ruft rm für jede ausgewählte Datei auf.
quelle
-1
ist die Standardeinstellung, wenn die Ausgabe in eine Pipeline erfolgt, daher ist dies hier nicht obligatorisch. Dies hat viel größere Probleme, die mit dem Standardverhaltenxargs
beim Parsen von Namen mit Leerzeichen, Anführungszeichen usw. zusammenhängen.--no-run-if-empty
in meiner Shell nicht erkannt wird. Ich benutze Cmder unter Windows.-0
Option verwendet werden, wenn Dateinamen Leerzeichen enthalten können. Habe es aber noch nicht getestet. QuelleErfordert GNU-Suche für -printf und GNU-Sortierung für -z und GNU awk für "\ 0" und GNU-xargs für -0, verarbeitet jedoch Dateien mit eingebetteten Zeilenumbrüchen oder Leerzeichen.
quelle
awk
Logik. Vermisse ich einige Anforderungen in der Frage des OP, die dies erforderlich machen?while read -r -d ' '; IFS= -r -d ''; do ...
Schleife zu leiten - der erste Lesevorgang endet im Leerzeichen, während der zweite im NUL fortgesetzt wird.sed -z -e 's/[^ ]* //; 1,5d'
am klarsten ist. (oder vielleichtsed -n -z -e 's/[^ ]* //; 6,$p'
.Alle diese Antworten schlagen fehl, wenn sich Verzeichnisse im aktuellen Verzeichnis befinden. Hier ist etwas, das funktioniert:
Dies:
funktioniert, wenn sich Verzeichnisse im aktuellen Verzeichnis befinden
versucht, jede Datei zu entfernen, auch wenn die vorherige nicht entfernt werden konnte (aufgrund von Berechtigungen usw.)
schlägt sicher fehl, wenn die Anzahl der Dateien im aktuellen Verzeichnis zu hoch ist und Sie
xargs
normalerweise verarschen würde (die-x
)berücksichtigt keine Leerzeichen in Dateinamen (möglicherweise verwenden Sie das falsche Betriebssystem?)
quelle
find
mehr Dateinamen zurückgegeben werden, als über eine einzelne Befehlszeile übergeben werden könnenls -t
? (Hinweis: Sie erhalten mehrere Läufe vonls -t
, von denen jeder nur einzeln sortiert ist, anstatt eine global korrekte Sortierreihenfolge zu haben. Daher ist diese Antwort bei der Ausführung mit ausreichend großen Verzeichnissen stark fehlerhaft.)Listen Sie die Dateinamen nach Änderungszeit auf und geben Sie dabei jeden Dateinamen an. Schließen Sie die ersten 3 (3 zuletzt) aus. Restliche entfernen.
BEARBEITEN Sie nach einem hilfreichen Kommentar von mklement0 (danke!): Das Argument -n + 3 wurde korrigiert. Beachten Sie, dass dies nicht wie erwartet funktioniert, wenn Dateinamen Zeilenumbrüche enthalten und / oder das Verzeichnis Unterverzeichnisse enthält.
quelle
-Q
Option scheint auf meinem Computer nicht vorhanden zu sein.-Q
. Ja,-Q
ist eine GNU-Erweiterung (hier ist die POSIX-ls
Spezifikation ). Eine kleine Einschränkung (in der Praxis selten ein Problem): Codiert-Q
eingebettete Zeilenumbrüche in Dateinamen als Literal\n
, dasrm
nicht erkannt wird. Um die ersten 3 auszuschließen ,xargs
muss das Argument+4
. Schließlich eine Einschränkung, die auch für die meisten anderen Antworten gilt: Ihr Befehl funktioniert nur dann wie beabsichtigt, wenn das aktuelle Verzeichnis keine Unterverzeichnisse enthält .--no-run-if-empty
Optionls -tQ | tail -n+4 | xargs --no-run-if-empty rm
Das Ignorieren von Zeilenumbrüchen ignoriert die Sicherheit und die gute Codierung. wnoise hatte die einzig gute Antwort. Hier ist eine Variation von ihm, die die Dateinamen in ein Array $ x einfügt
quelle
IFS
- andernfalls besteht die Gefahr, dass Leerzeichen aus Dateinamen verloren gehen. Kann das auf den Lesebefehl übertragen:while IFS= read -rd ''; do
"${REPLY#* }"
?Wenn die Dateinamen keine Leerzeichen enthalten, funktioniert dies:
Wenn die Dateinamen Leerzeichen haben, so etwas wie
Grundlegende Logik:
quelle
while read
Trick für den Umgang mit Leerzeichen:ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
while IFS= read -r d
wäre ein bisschen besser - das-r
verhindert, dass Backslash-Literale verbraucht werdenread
, und dasIFS=
verhindert das automatische Trimmen von nachgestellten Leerzeichen.touch $'hello \'$(rm -rf ~)\' world'
. Die Literal-Anführungszeichen im Dateinamen würden den von Ihnen hinzugefügten Literal-Anführungszeichen entgegenwirkensed
, was dazu führen würde , dass der Code im Dateinamen ausgeführt wird.| sh
Formular, das die Sicherheitsanfälligkeit bezüglich der Shell-Injektion aufweist).Mit zsh
Angenommen, Sie interessieren sich nicht für aktuelle Verzeichnisse und haben nicht mehr als 999 Dateien (wählen Sie eine größere Anzahl, wenn Sie möchten, oder erstellen Sie eine while-Schleife).
In
*(.om[6,999])
den.
Mittelwertdateien, dero
Sortierreihenfolge der Mittelwerte, demm
Mittelwert nach Änderungsdatum (a
für die Zugriffszeit oderc
für den Inode-Wechsel) wird[6,999]
ein Dateibereich ausgewählt, sodass die 5 nicht zuerst angezeigt werden.quelle
om
) nicht zum Laufen bringen (jede Sortierung, die ich versucht habe, zeigte keine Wirkung - auch nicht unter OSX 10.11.2 (versucht mit zsh 5.0.8 und 5.1.1) , noch unter Ubuntu 14.04 (zsh 5.0.2)) - was fehlt mir?. Was den Bereichsendpunkt betrifft: Sie müssen ihn nicht fest codieren. Verwenden Sie ihn einfach-1
, um auf den letzten Eintrag zu verweisen und damit alle verbleibenden Dateien einzuschließen :[6,-1]
.Mir ist klar, dass dies ein alter Thread ist, aber vielleicht wird jemand davon profitieren. Dieser Befehl findet Dateien im aktuellen Verzeichnis:
Dies ist etwas robuster als einige der vorherigen Antworten, da Ihre Suchdomäne auf Dateien beschränkt werden kann, die mit Ausdrücken übereinstimmen. Suchen Sie zunächst nach Dateien, die den gewünschten Bedingungen entsprechen. Drucken Sie diese Dateien mit den Zeitstempeln daneben.
Sortieren Sie sie anschließend nach den Zeitstempeln:
Dann streichen Sie die 4 neuesten Dateien aus der Liste ab:
Nimm die 2. Spalte (den Dateinamen, nicht den Zeitstempel):
Und dann packen Sie das Ganze in eine for-Aussage:
Dies mag ein ausführlicherer Befehl sein, aber ich hatte viel besseres Glück, bedingte Dateien als Ziel festlegen und komplexere Befehle gegen sie ausführen zu können.
quelle
fand interessante cmd in Sed-Onliners - Löschen Sie die letzten 3 Zeilen - und finden Sie es perfekt für eine andere Art, die Katze zu häuten (okay, nicht), aber Idee:
quelle
Entfernt alle bis auf die 10 neuesten (die meisten neueren) Dateien
Wenn weniger als 10 Dateien vorhanden sind, wird keine Datei entfernt und Sie haben: Fehlerkopf: unzulässige Zeilenanzahl - 0
Dateien mit Bash zählen
quelle
Ich brauchte eine elegante Lösung für die Busybox (Router), alle Xargs- oder Array-Lösungen waren für mich nutzlos - dort war kein solcher Befehl verfügbar. find and mtime ist nicht die richtige Antwort, da es sich um 10 Elemente 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 Standardmethode gelöst:
Etwas lehrreichere Version: Wir können alles machen, wenn wir awk anders verwenden. Normalerweise verwende ich diese Methode, um Variablen von der awk an die sh zu übergeben (zurückzugeben). Da wir die ganze Zeit lesen, was nicht möglich ist, möchte ich mich unterscheiden: Hier ist die Methode.
Beispiel für .tar-Dateien ohne Probleme hinsichtlich der Leerzeichen im Dateinamen. Ersetzen Sie zum Testen "rm" durch "ls".
Erläuterung:
ls -td *.tar
listet alle .tar-Dateien nach 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ügere Verwendung ist:Im Falle der Verwendung von
ls -t
Befehl wird solchen dummen Beispielen wie:touch 'foo " bar'
und kein Schaden zugefügttouch '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 Druck (einfache Form, keine Leerzeichen toleriert):
um die Variable
VarName
auf den Wert von zu setzen$1
. Es können mehrere Variablen auf einmal erstellt werden. DiesVarName
wird zu einer normalen sh-Variablen und kann anschließend normalerweise in einem Skript oder einer Shell verwendet werden. So erstellen Sie Variablen mit awk und geben sie an die Shell zurück:quelle
quelle
xargs
ohne-0
oder zumindest-d $'\n'
ist unzuverlässig; Beobachten Sie, wie sich dies bei einer Datei verhält, deren Name Leerzeichen oder Anführungszeichen enthält.Ich habe daraus ein Bash-Shell-Skript gemacht. Verwendung:
keep NUM DIR
Dabei ist NUM die Anzahl der zu speichernden Dateien und DIR das zu bereinigende Verzeichnis.quelle