Löschen Sie alle bis auf die neuesten X-Dateien in Bash

157

Gibt es eine einfache Möglichkeit, in einer ziemlich normalen UNIX-Umgebung mit bash einen Befehl auszuführen, um alle bis auf die neuesten X-Dateien aus einem Verzeichnis zu löschen?

Um ein konkreteres Beispiel zu geben, stellen Sie sich vor, ein Cron-Job schreibt stündlich eine Datei (z. B. eine Protokolldatei oder eine gezielte Sicherung) in ein Verzeichnis. Ich möchte einen anderen Cron-Job ausführen, bei dem die ältesten Dateien in diesem Verzeichnis entfernt werden, bis weniger als beispielsweise 5 vorhanden sind.

Und um ganz klar zu sein, es ist nur eine Datei vorhanden, sie sollte niemals gelöscht werden.

Matt Sheppard
quelle

Antworten:

117

Die Probleme mit den vorhandenen Antworten:

  • Unfähigkeit, Dateinamen mit eingebetteten Leerzeichen oder Zeilenumbrüchen zu behandeln.
    • Bei Lösungen, die rmdirekt 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 rmauf 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 lsAusgabe zu analysieren : http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

Die oben ist ineffizient , weil xargsaufgerufen hat rmeinmal für jeden Dateinamen.
Mit Ihrer Plattform xargskönnen Sie möglicherweise dieses Problem lösen:

Wenn Sie GNU xargs , Verwendung -d '\n', die Marken xargsjede 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 rmnicht aufgerufen wird, wenn keine Eingabe erfolgt.

Wenn Sie BSD xargs (einschließlich auf macOS ), können Sie -0behandeln 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 -tpdruckt 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 NDateien an übergeben werden N+1muss tail -n +.
  • xargs -I {} rm -- {}(und seine Variationen) ruft dann rmalle diese Dateien auf; Wenn es überhaupt keine Übereinstimmungen gibt, xargswird nichts getan.
    • xargs -I {} rm -- {}Definiert einen Platzhalter {}, der jede Eingabezeile als Ganzes darstellt . Er rmwird 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
mklement0
quelle
2
Sicherlich besser als die meisten anderen Antworten hier, daher unterstütze ich Sie gerne, auch wenn ich das Ignorieren des Newline-Falls als eine Sache betrachte, die nur mit Vorsicht zu tun ist.
Charles Duffy
2
Wenn Sie sich lsnicht im aktuellen Verzeichnis befinden, enthalten die Pfade zu Dateien '/', was bedeutet, dass grep -v '/'nichts übereinstimmt. Ich glaube, grep -v '/$'Sie möchten nur Verzeichnisse ausschließen.
Waldol1
1
@ Waldol1: Danke; Ich habe die Antwort aktualisiert, um Ihren Vorschlag aufzunehmen, wodurch der grepBefehl auch konzeptionell klarer wird. Beachten Sie jedoch, dass das von Ihnen beschriebene Problem nicht mit einem einzigen Verzeichnispfad aufgetreten wäre . zB ls -p /private/varwü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. zB ls -p /private/var/*(und Sie würden auch den Inhalt übereinstimmender Unterverzeichnisse sehen, es sei denn, Sie haben auch eingeschlossen -d).
mklement0
108

Entfernen Sie alle bis auf 5 (oder eine beliebige Anzahl) der neuesten Dateien in einem Verzeichnis.

rm `ls -t | awk 'NR>5'`
Espo
quelle
2
Ich brauchte dies, um nur meine Archivdateien zu berücksichtigen. Wechsel ls -tzuls -td *.bz2
James T Snell
3
Ich habe dies für Verzeichnisse verwendet, indem ich es in rm -rf geändert habe ls -t | awk 'NR>1'(ich wollte nur das neueste). Vielen Dank!
Lohiaguitar91
10
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.
H2ONaCl
16
Prägnant und lesbar vielleicht, aber gefährlich zu bedienen; Wenn Sie versuchen, eine mit erstellte Datei zu touch 'hello * world'löschen, wird absolut alles im aktuellen Verzeichnis gelöscht .
Charles Duffy
1
Obwohl dies 2008 beantwortet wurde, funktioniert es wie ein Zauber und genau das, was ich brauchte, um alte Backups einfach aus einem bestimmten Verzeichnis zu löschen. Genial.
Rens Tillmann
86
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm

Diese Version unterstützt Namen mit Leerzeichen:

(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
thelsdj
quelle
20
Dieser Befehl verarbeitet Dateien mit Leerzeichen in den Namen nicht korrekt.
Tyler
5
(ls -t|head -n 5;ls)ist eine Befehlsgruppe . Die 5 neuesten Dateien werden zweimal gedruckt. sortfügt identische Zeilen zusammen. uniq -uEntfernt Duplikate, sodass alle bis auf die 5 neuesten Dateien erhalten bleiben. xargs rmruft rmjeden von ihnen an.
Fabien
15
Dies löscht alle Ihre Dateien, wenn Sie 5 oder weniger haben! In --no-run-if-emptyzu xargswie in (ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rmbitte die Antwort aktualisieren.
Gonfi den Tschal
3
Sogar derjenige, der "Namen mit Leerzeichen unterstützt", ist gefährlich. Stellen Sie sich einen Namen vor, der wörtliche Anführungszeichen enthält: touch 'foo " bar'Der gesamte Rest des Befehls wird entfernt.
Charles Duffy
2
... es ist sicherer in der Anwendung xargs -d $'\n'als einzuspritzen Zitate in Ihre Inhalte, wenn die Eingangsstrom NUL-abgrenzt (die als die Verwendung von etwas anderes erfordert , lsum wirklich richtig tun) ist die ideale Wahl.
Charles Duffy
59

Einfachere Variante der Antwort von thelsdj:

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

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.

Fabien
quelle
15
Sie müssen --no-run-if-empty zu xargs hinzufügen, damit es nicht fehlschlägt, wenn weniger als 5 Dateien vorhanden sind.
Tom
ls -1tr | Kopf -n -5 | xargs rm <---------- Sie müssen eine -1 zum ls hinzufügen, sonst erhalten Sie keine Listenausgabe, gegen die head ordnungsgemäß arbeiten kann
Al Joslin
3
@AlJoslin -1ist die Standardeinstellung, wenn die Ausgabe in eine Pipeline erfolgt, daher ist dies hier nicht obligatorisch. Dies hat viel größere Probleme, die mit dem Standardverhalten xargsbeim Parsen von Namen mit Leerzeichen, Anführungszeichen usw. zusammenhängen.
Charles Duffy
scheint, dass das --no-run-if-emptyin meiner Shell nicht erkannt wird. Ich benutze Cmder unter Windows.
StayFoolish
Möglicherweise muss die -0Option verwendet werden, wenn Dateinamen Leerzeichen enthalten können. Habe es aber noch nicht getestet. Quelle
Keith
18
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-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.

wnoise
quelle
2
Wenn Sie Verzeichnisse entfernen möchten, ändern Sie einfach das -f in ein -d und fügen Sie dem rm ein -r hinzu. finden . -maxdepth 1 -type d -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 -rf
Alex
1
Auf einen Blick bin ich überrascht über die Komplexität (oder im Übrigen die Notwendigkeit) der awkLogik. Vermisse ich einige Anforderungen in der Frage des OP, die dies erforderlich machen?
Charles Duffy
@ Charles Duffy: Das sub () entfernt den Zeitstempel, nach dem sortiert ist. Der von "% T @" erzeugte Zeitstempel kann einen Bruchteil enthalten. Das Aufteilen auf einen Raum mit FS unterbricht Pfade mit eingebetteten Räumen. Ich nehme an, das Entfernen durch den ersten Raum funktioniert, ist aber fast genauso schwer zu lesen. Die RS- und ORS-Trennzeichen können nicht in der Befehlszeile festgelegt werden, da es sich um NULs handelt.
wnoise
1
@wnoise, mein üblicher Ansatz besteht darin, in eine Shell- 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.
Charles Duffy
@ Charles Duffy: Ich bin immer misstrauisch gegenüber rohen Muscheln, vielleicht aufgrund byzantinischer Zitierbedenken. Ich denke jetzt, dass GNU sed -z -e 's/[^ ]* //; 1,5d'am klarsten ist. (oder vielleicht sed -n -z -e 's/[^ ]* //; 6,$p'.
wnoise
14

Alle diese Antworten schlagen fehl, wenn sich Verzeichnisse im aktuellen Verzeichnis befinden. Hier ist etwas, das funktioniert:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

Dies:

  1. funktioniert, wenn sich Verzeichnisse im aktuellen Verzeichnis befinden

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

  3. schlägt sicher fehl, wenn die Anzahl der Dateien im aktuellen Verzeichnis zu hoch ist und Sie xargsnormalerweise verarschen würde (die-x )

  4. berücksichtigt keine Leerzeichen in Dateinamen (möglicherweise verwenden Sie das falsche Betriebssystem?)

Charles Wood
quelle
5
Was passiert, wenn findmehr Dateinamen zurückgegeben werden, als über eine einzelne Befehlszeile übergeben werden können ls -t? (Hinweis: Sie erhalten mehrere Läufe von ls -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.)
Charles Duffy
12
ls -tQ | tail -n+4 | xargs rm

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.

Kennzeichen
quelle
Die -QOption scheint auf meinem Computer nicht vorhanden zu sein.
Pierre-Adrien Buisson
4
Hmm, die Option ist seit ~ 20 Jahren in GNU-Core-Utils enthalten, wird jedoch in BSD-Varianten nicht erwähnt. Bist du auf einem Mac?
Mark
Ich bin in der Tat. Ich hätte nicht gedacht, dass es Unterschiede für diese Art von wirklich grundlegenden Befehlen zwischen aktuellen Systemen gibt. Danke für deine Antwort !
Pierre-Adrien Buisson
3
@Mark: ++ für -Q. Ja, -Qist eine GNU-Erweiterung (hier ist die POSIX- lsSpezifikation ). Eine kleine Einschränkung (in der Praxis selten ein Problem): Codiert-Q eingebettete Zeilenumbrüche in Dateinamen als Literal \n, das rmnicht erkannt wird. Um die ersten 3 auszuschließen , xargsmuss 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 .
mklement0
1
Wenn es nichts zu entfernen gibt, haben Sie xargs mit der --no-run-if-emptyOption ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
angerufen
8

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

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
Ian Kelling
quelle
2
Ich würde das Löschen vorschlagen IFS- andernfalls besteht die Gefahr, dass Leerzeichen aus Dateinamen verloren gehen. Kann das auf den Lesebefehl übertragen:while IFS= read -rd ''; do
Charles Duffy
1
warum "${REPLY#* }"?
msciwoj
4

Wenn die Dateinamen keine Leerzeichen enthalten, funktioniert dies:

ls -C1 -t| awk 'NR>5'|xargs rm

Wenn die Dateinamen Leerzeichen haben, so etwas wie

ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh

Grundlegende Logik:

  • Holen Sie sich eine Liste der Dateien in zeitlicher Reihenfolge, eine Spalte
  • Holen Sie sich alle bis auf die ersten 5 (n = 5 für dieses Beispiel)
  • erste version: sende diese an rm
  • zweite Version: Gen ein Skript, das sie richtig entfernt
Mark Harrison
quelle
Vergessen Sie nicht den while readTrick für den Umgang mit Leerzeichen: ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
Pinkeen
1
@pinkeen, nicht ganz sicher wie dort angegeben. while IFS= read -r dwäre ein bisschen besser - das -rverhindert, dass Backslash-Literale verbraucht werden read, und das IFS=verhindert das automatische Trimmen von nachgestellten Leerzeichen.
Charles Duffy
4
Übrigens, wenn man sich Sorgen um feindliche Dateinamen macht, ist dies ein äußerst gefährlicher Ansatz. Betrachten Sie eine Datei, die mit erstellt wurde touch $'hello \'$(rm -rf ~)\' world'. Die Literal-Anführungszeichen im Dateinamen würden den von Ihnen hinzugefügten Literal-Anführungszeichen entgegenwirken sed, was dazu führen würde , dass der Code im Dateinamen ausgeführt wird.
Charles Duffy
1
(Um klar zu sein, bezog sich das "dies" oben auf das | shFormular, das die Sicherheitsanfälligkeit bezüglich der Shell-Injektion aufweist).
Charles Duffy
2

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

[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])

In *(.om[6,999])den .Mittelwertdateien, der oSortierreihenfolge der Mittelwerte, dem mMittelwert nach Änderungsdatum ( afür die Zugriffszeit oder cfür den Inode-Wechsel) wird [6,999]ein Dateibereich ausgewählt, sodass die 5 nicht zuerst angezeigt werden.

lolesque
quelle
Faszinierend, aber für mein Leben konnte ich das Sorting Glob Qualifier ( 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].
mklement0
2

Mir ist klar, dass dies ein alter Thread ist, aber vielleicht wird jemand davon profitieren. Dieser Befehl findet Dateien im aktuellen 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 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.

find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'

Sortieren Sie sie anschließend nach den Zeitstempeln:

sort -r -z -n

Dann streichen Sie die 4 neuesten Dateien aus der Liste ab:

tail -n+5

Nimm die 2. Spalte (den Dateinamen, nicht den Zeitstempel):

awk '{ print $2; }'

Und dann packen Sie das Ganze in eine for-Aussage:

for F in $(); do rm $F; done

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.

TopherGopher
quelle
1

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:

 #!/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
tim
quelle
1

Entfernt alle bis auf die 10 neuesten (die meisten neueren) Dateien

ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm

Wenn weniger als 10 Dateien vorhanden sind, wird keine Datei entfernt und Sie haben: Fehlerkopf: unzulässige Zeilenanzahl - 0

Dateien mit Bash zählen

fabrice
quelle
1

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:

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

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

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

Erläuterung:

ls -td *.tarlistet alle .tar-Dateien nach 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ügere Verwendung ist:

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

Im Falle der Verwendung von ls -tBefehl 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):

print "VarName="$1

um die Variable VarNameauf den Wert von zu setzen $1. Es können mehrere Variablen auf einmal erstellt werden. Dies VarNamewird 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:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\""  }'); echo "$VarName"
Pila
quelle
0
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
Pavel Tankov
quelle
2
xargsohne -0oder zumindest -d $'\n'ist unzuverlässig; Beobachten Sie, wie sich dies bei einer Datei verhält, deren Name Leerzeichen oder Anführungszeichen enthält.
Charles Duffy
0

Ich habe daraus ein Bash-Shell-Skript gemacht. Verwendung: keep NUM DIRDabei ist NUM die Anzahl der zu speichernden Dateien und DIR das zu bereinigende Verzeichnis.

#!/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
Rohrkolben
quelle