Das ganze Drehbuch.

Das ganze Drehbuch.

Ich habe ein Skript, mit dem ich alte Dateien löschen möchte, wenn deren Anzahl eine festgelegte Grenze überschreitet.

Ich habe diesen Befehl:

/bin/rm -f `/bin/ls -t $bkup_p/mysql.daily/*  2> /dev/null | /bin/awk 'NR>'5`

das funktioniert und da $bkup_p ein Leerzeichen enthalten könnte, habe ich versucht, es zu ändern in

/bin/rm -f `/bin/ls -t "$bkup_p/mysql.daily/*"  2> /dev/null | /bin/awk 'NR>'5`

Aber das funktioniert nicht. Es zeigt die betreffenden Dateien nicht an, es ist einfach leer

Antwort1

Bash ist nicht sehr praktisch, wenn es darum geht, Dateien nach Änderungszeit zu filtern und Befehle darauf auszuführen.

Ich würde empfehlen, stattdessen z-shell auszuprobieren, also zuerst ausführen zshund dann

rm -i -- "$bkup_p"/mysql.daily/*(DN.Om[1,5])

Dadurch werden die ältesten fünf einfachen Dateien im angegebenen Verzeichnis gelöscht. Ich vermute, dass dies wahrscheinlich das ist, was Sie erreichen möchten. Ändern Sie dies am Ende des Tages natürlich rm -ibei rm -fBedarf in .

Um die fünf neuesten Dateien zu entfernen,

rm -i -- "$bkup_p"/mysql.daily/*(DN.om[1,5])

Nun zur Funktionsweise. Alles darin ()sind sogenannte Glob-Qualifizierer, die im Wesentlichen Dateien je nach Bedarf filtern:

  • DDotfiles einschließen (die mit beginnen .)
  • Nkeinen Fehler melden, wenn keine Übereinstimmungen vorliegen
  • .wählt nur einfache Dateien aus
  • omsortiert nach Änderungszeitpunkt ( Omsortiert in umgekehrter Reihenfolge)
  • [1,5]wählt nur fünf Dateien aus der Liste aus

Ich glaube, das alles sollte auch mit Sonderzeichen in Dateinamen (Leerzeichen, Zeilenumbrüche usw.) funktionieren.

Antwort2

Wie andere bereits gesagt haben, sollten Sie lsdafür nicht verwenden. Aber wenn Ihre Umgebung nichts Sinnvolleres bietet und SieKennen Sie die Vorbehalte, Dukönntedamit durchkommen, wenn Ihre Dateinamen sindnett genug: keine Leerzeichen oder nicht druckbaren Zeichen und insbesondere nichts, was die Shell als Globbing-Zeichen interpretiert (zumindest ?*[]).

Besser wäre es, zum Sortieren der Dateien etwas wie Perl zu verwenden oder die Daten in die Dateinamen einzufügen, damit Sie sich auf die globale Reihenfolge verlassen können.


Vor diesem Hintergrund: In Ihrem zweiten Snippet ist das Asterisk in Anführungszeichen gesetzt, sodass kein Globbing stattfindet. Sie würden einen Fehler über das some dir/mysql.daily/*Nichtvorhandensein sehen, aber das ist nicht der Fall, da Sie die Fehlerausgabe umgeleitet haben.

Bei Dateinamen mit Leerzeichen tritt das Problem auf, dass die Ausgabe der Befehlsersetzung entlang der Leerzeichen aufgeteilt wird. Es wird also etwa to in zwei Teilen some dir/fooausgegeben : und . Hoffentlich existiert keiner von beiden, da sie sonst entfernt werden.rmsomedir/foo

Wenn nur Ihr Verzeichnisname Leerzeichen enthält, können Sie cdes zuerst eingeben. Oder stellen Sie es so ein IFS, dass es nur eine neue Zeile enthält (anstelle der standardmäßigen Leerzeichen, Tabulatoren und Zeilenumbrüche). Sie haben ein NAS erwähnt, also haben Sie wahrscheinlich eine Busybox. Dies sollte sowohl in Busybox als auch in Bash funktionieren und die Dateinamen ausgeben, einen pro Zeile:

IFS=$'\n'
printf "%s\n" $( ls -t "$bkup_p/mysql.daily/"* | tail -n +6 )

Ersetzen Sie es printf "%s\n"durch , rmum die Dateien zu entfernen, wenn Sie sicher sind, dass es richtig funktioniert.

Antwort3

Ich würde davon abraten, die Ausgabe zu analysieren, lsda sie die Ausgabe nicht richtig für die Weiterleitung in einen neuen Befehl formatiert und Portabilitätsprobleme aufweist.
Ich würde stattdessen so etwas versuchen

find $bkup_p/mysql.daily/ -type f -a -mtime +7 -a -name "*.sql" -a -exec rm -f {} +

Notiz:

  • "*.sql"ändern Sie dies nach Bedarf
  • -mtime +7bedeutet *wenn diese Datei vor mehr (+) als 7 Tagen geändert wurde, ändern Sie dies natürlich auch bei Bedarf

Wenn Sie sichergehen wollen, dass Sie immer die 10 neuesten Dateien haben - egal was passiert (und Sie GNU haben find), könnten Sie versuchen

find $bkup_p/mysql.daily/ -maxdepth 1 -type f -a -printf "%T+\t%p\n" | sort -r | sed -n '10,$p' | awk '{print $2}' | xargs rm -f

Weitere Informationen zum Formatieren findder Ausgabe mit dem -printfOperator finden Sie unter

man find | less '+/^\s*-printf'

Antwort4

Das Problem, dass es *nicht funktioniert, besteht darin, dass es sich beim Zitieren nicht erweitert.

Vergleichen Sie die Ergebnisse hiervon: echo *mit diesem: echo "*".


Die robuste Lösung besteht darin, ein Array zu verwenden, eine Datei an jeder Array-Position.

Wenn Sie Glück haben und Folgendes unterstützen -printf:

dir="$bkup_p/mysql.daily/"

find "$dir" -printf '%T@:%i\n'

Alle Dateinamen werden vermieden. Jede Datei besteht aus einer Zeile. Und zwar einer Zeile, die nur aus Zahlen, einem optionalen Punkt und einem Doppelpunkt besteht.

Das ließe sich leicht lösen:

find "$dir" -printf '%T@:%i\n' | sort -rn

Das einfachste Array sind die Positionsargumente.
Dadurch werden alle Dateien im Verzeichnis auf die Liste der Positionsargumente gesetzt:

set -f; set -- $(find "$dir" -printf '%T@:%i\n' | sort -n )

Mit set -f wird die Erweiterung aller Dateinamen vermieden, die auch Platzhalter sind *. Um die ersten 6 Dateinamen zu vermeiden (entfernen), führen Sie einfach shiftdiese aus:

shift 5

Und machen Sie mit den verbleibenden Dateien das Nötige:

for    f
do     rm -f "$(find "$dir" -inum "${f#*:}")"
done

Das ganze Drehbuch.

Das gesamte Skript (robust für jeden Dateinamen) lautet:

dir="$bkup_p/mysql.daily/"

set -f
set -- $(find "$dir" -type f -printf '%T@:%i\n'|sort -rn)
shift 5

for    f
do     rm -f "$(find "$dir" -inum "${f#*:}")"
done

verwandte Informationen