Wie schreibe ich ein Skript, um nur die 20 ältesten Dateien von einem Ordner in einen anderen zu verschieben? Gibt es eine Möglichkeit, die ältesten Dateien in einem Ordner zu erfassen?
bash
shell
shell-script
files
timestamps
user11598
quelle
quelle
atime
(letzter Zugriff),ctime
(letzte Berechtigungsänderung) undmtime
(letzte Änderung) ... zB.ls -t
und find'sprintf "%T"
usemtime
... Laut diesem Link sind meineext4
Partitionen in der Lage , ein Erstellungsdatum zu verarbeiten, aberls
undfind
undstat
haben (noch) nicht die entsprechenden Optionen ...Antworten:
Das Parsen der Ausgabe von
ls
ist nicht zuverlässig .Verwenden Sie stattdessen,
find
um die Dateien zu lokalisieren undsort
nach Zeitstempel zu ordnen. Beispielsweise:Was macht das alles?
Zuerst
find
sucht der Befehl alle Dateien und Verzeichnisse im aktuellen Verzeichnis (.
), jedoch nicht in den Unterverzeichnissen des aktuellen Verzeichnisses (-maxdepth 1
), und gibt dann Folgendes aus:Der Zeitstempel ist wichtig. Der
%T@
Formatbezeichner für-printf
zerfällt inT
"Letzte Änderungszeit" der Datei (mtime) und@
"Sekunden seit 1970", einschließlich Sekundenbruchteile.Das Leerzeichen ist lediglich ein beliebiger Begrenzer. Der vollständige Pfad zur Datei ist so, dass wir später darauf verweisen können, und das NULL-Zeichen ist ein Abschlusszeichen, da es sich um ein unzulässiges Zeichen in einem Dateinamen handelt. Auf diese Weise können wir sicher sein, dass wir das Ende des Pfads zur Datei erreicht haben Datei.
Ich habe eingeschlossen,
2>/dev/null
damit Dateien, auf die der Benutzer keine Zugriffsberechtigung hat, ausgeschlossen werden, aber Fehlermeldungen darüber, dass sie ausgeschlossen werden, unterdrückt werden.Das Ergebnis des
find
Befehls ist eine Liste aller Verzeichnisse im aktuellen Verzeichnis. Die Liste wird an Folgendes weitergeleitetsort
:-z
Behandle NULL als Zeilenendezeichen anstelle von Newline.-n
Numerisch sortierenDa die Sekunden seit 1970 immer höher sind, möchten wir die Datei, deren Zeitstempel die kleinste Zahl war. Das erste Ergebnis von
sort
ist die Zeile mit dem kleinsten nummerierten Zeitstempel. Alles was bleibt ist, den Dateinamen zu extrahieren.Die Ergebnisse der
find
,sort
Pipeline wird über geleitet Prozess Substitution zu ,while
wo es gelesen wird , als ob es eine Datei auf stdin waren.while
Ruft wiederum aufread
, um die Eingabe zu verarbeiten.Im Kontext von setzen
read
wir dieIFS
Variable auf nichts, was bedeutet, dass Leerzeichen nicht unangemessen als Trennzeichen interpretiert werden.read
erzählt wird-r
, welche escape Expansions deaktiviert, und-d $'\0'
, was die End-of-line - Trennzeichen NULL, Anpassen der Ausgabe aus unserer machtfind
,sort
Pipeline.Der erste Datenblock, der den ältesten Dateipfad mit vorangestelltem Zeitstempel und Leerzeichen darstellt, wird in die Variable eingelesen
line
. Als nächstes wird die Parameterersetzung mit dem Ausdruck verwendet#*
, der einfach alle Zeichen vom Anfang der Zeichenfolge bis zum ersten Leerzeichen, einschließlich des Leerzeichens, durch nichts ersetzt. Dadurch wird der Änderungszeitstempel entfernt, und es verbleibt nur der vollständige Pfad zur Datei.Zu diesem Zeitpunkt ist der Dateiname in gespeichert
$file
und Sie können alles tun, was Sie wollen. Wenn Sie mit$file
derwhile
Anweisung fertig sind, wird eine Schleife ausgeführt, und derread
Befehl wird erneut ausgeführt, wobei der nächste Block und der nächste Dateiname extrahiert werden.Gibt es nicht einen einfacheren Weg?
Einfachere Wege sind fehlerhaft.
Wenn Sie eine
ls -t
Pipe zuhead
odertail
(oder etwas anderem ) verwenden, brechen Sie bei Dateien mit Zeilenumbrüchen in den Dateinamen ab. Wenn Siemv $(anything)
dann Dateien mit Leerzeichen im Namen verursachen Bruch. Wenn Siemv "$(anything)"
dann Dateien mit nachgestellten Zeilenumbrüchen im Namen erstellen, wird dies zu einem Bruch führen. Wenn Sie diesread
nicht-d $'\0'
tun, brechen Sie bei Dateien mit Leerzeichen im Namen ab.Vielleicht wissen Sie in bestimmten Fällen, dass ein einfacherer Weg ausreicht, aber Sie sollten solche Annahmen niemals in Skripte schreiben, wenn Sie dies vermeiden können.
Lösung
Rufen Sie an wie:
Verschieben der ältesten 20 Dateien von
/var/log/foo/
nach/mnt/backup/
.Beachten Sie, dass ich Dateien und Verzeichnisse einbinde. Für Dateien nur
-type f
zumfind
Aufruf hinzufügen .Vielen Dank
Vielen Dank an Enzotib und Павел Танков für die Verbesserung dieser Antwort.
quelle
-n
. Zumindest in meiner Version werden Dezimalzahlen nicht richtig sortiert. Sie müssen entweder den Punkt im Datum entfernen oder-printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz
ISO-Daten oder etwas anderes verwenden..
Muss hinaus ist für Sie irrelevant.) Es wäre klarer zu sagensort -z -n -t. -k1
.%TS
Zeigt auch einen "Bruchteil" an, der in der Form wäre00.0000000000
, sodass Sie auch die Granularität verlieren. Neuere GNU- Versionensort
könnten dieses Problem lösen, indem sie-V
eine "Versionssortierung" verwenden, die diesen Gleitkommatyp erwartungsgemäß handhabt.%T@
würde dann auch funktionieren, da sie mit Nullen aufgefüllt ist.Am einfachsten ist es in zsh, wo Sie das
Om
Glob-Qualifikationsmerkmal verwenden können , um Übereinstimmungen nach Datum (älteste zuerst) zu sortieren, und das[1,20]
Qualifikationsmerkmal, um nur die ersten 20 Übereinstimmungen beizubehalten:Fügen Sie das
D
Qualifikationsmerkmal hinzu, wenn Sie auch Punktdateien einschließen möchten. Hinzufügen,.
wenn Sie nur normale Dateien und keine Verzeichnisse abgleichen möchten.Wenn Sie kein zsh haben, ist hier ein Perl-Einzeiler (Sie können es in weniger als 80 Zeichen, aber mit einem weiteren Aufwand an Klarheit tun):
Mit nur POSIX-Tools oder sogar bash oder ksh ist das Sortieren von Dateien nach Datum ein Problem. Sie können dies problemlos tun
ls
, aber das Parsen der Ausgabe vonls
ist problematisch. Daher funktioniert dies nur, wenn die Dateinamen nur druckbare Zeichen enthalten, die keine Zeilenumbrüche sind.quelle
ls -t
Ausgabe mittail
oder kombinierenhead
.Einfaches Beispiel, das nur funktioniert, wenn alle Dateinamen nur druckbare Zeichen außer Leerzeichen enthalten und
\[*?
:quelle
ls -1Atr
touch $'foo\n*'
. Was passiert, wenn Sie mv "$ (ls)" ausführen, während sich diese Datei dort befindet?Sie können GNU find dafür verwenden:
Wenn find die Änderungszeit (in Sekunden ab 1970) und den Namen jeder Datei des aktuellen Verzeichnisses ausgibt, wird die Ausgabe nach dem ersten Feld sortiert, die 20 ältesten werden gefiltert und nach verschoben
dest_dir
. Entfernen Sie das,echo
wenn Sie die Befehlszeile getestet haben.quelle
Niemand hat (noch) ein Bash-Beispiel gepostet, das eingebettete Zeilenumbrüche (eingebettete Elemente) im Dateinamen berücksichtigt. Es werden die 3 ältesten (mdate) regulären Dateien verschoben
Dies ist das Testdaten- Snippet
Hier ist die Check-Ergebnisse Snippet
quelle
Es ist am einfachsten mit GNU zu tun
find
. Ich verwende es jeden Tag auf meinem Linux-DVR, um Aufzeichnungen von meinem Videoüberwachungssystem zu löschen, die älter als ein Tag sind.Hier ist die Syntax:
Denken Sie daran, dass
find
ein Tag als 24 Stunden ab dem Zeitpunkt der Ausführung definiert wird. Daher werden Dateien, die zuletzt um 23 Uhr geändert wurden, nicht um 1 Uhr morgens gelöscht.Sie können sogar kombinieren
find
mitcron
, kann so Löschungen automatisch durch den folgenden Befehl als root ausgeführt wird, statt:Weitere Informationen erhalten Sie immer auf
find
der Manualpage:quelle
Da die anderen Antworten nicht zu meinem und dem Zweck der Fragen passen, wird diese Shell unter CentOS 7 getestet:
quelle