
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 zsh
und 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 -i
bei rm -f
Bedarf 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:
D
Dotfiles einschließen (die mit beginnen.
)N
keinen Fehler melden, wenn keine Übereinstimmungen vorliegen.
wählt nur einfache Dateien ausom
sortiert nach Änderungszeitpunkt (Om
sortiert 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 ls
dafü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/foo
ausgegeben : und . Hoffentlich existiert keiner von beiden, da sie sonst entfernt werden.rm
some
dir/foo
Wenn nur Ihr Verzeichnisname Leerzeichen enthält, können Sie cd
es 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 , rm
um die Dateien zu entfernen, wenn Sie sicher sind, dass es richtig funktioniert.
Antwort3
Ich würde davon abraten, die Ausgabe zu analysieren, ls
da 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 +7
bedeutet *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 find
der Ausgabe mit dem -printf
Operator 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 shift
diese 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