it-swarm.com.de

Wie finde ich die älteste Datei in einem Verzeichnisbaum?

Ich suche einen Shell-Einzeiler, um die älteste Datei in einem Verzeichnisbaum zu finden.

70
Marius Gedminas

Dies funktioniert (aktualisiert, um den Vorschlag von Daniel Andersson zu berücksichtigen):

find -type f -printf '%T+ %p\n' | sort | head -n 1
72
Marius Gedminas

Dies ist etwas portabler und weil es nicht auf der GNU find Erweiterung -printf basiert, funktioniert es auch unter BSD/OS X:

find . -type f -print0 | xargs -0 ls -ltr | head -n 1

Der einzige Nachteil hier ist, dass es etwas auf die Größe von ARG_MAX beschränkt ist (wobei für die meisten neueren Kernel irrelevant sein sollte). Wenn also mehr als getconf ARG_MAX Zeichen zurückgegeben werden (262,144 auf meinem System), erhalten Sie nicht das richtige Ergebnis. Es ist auch nicht POSIX-konform, da -print0 und xargs -0 dies nicht sind.

Weitere Lösungen für dieses Problem finden Sie hier: Wie kann ich die neueste (neueste, älteste, älteste) Datei in einem Verzeichnis finden? - Gregs Wiki

11
slhck

Die folgenden Befehle funktionieren garantiert mit jeder Art von seltsamen Dateinamen:

find -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat

find -type f -printf "%[email protected] %T+ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'

stat -c "%y %n" "$(find -type f -printf "%[email protected] %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"

Die Verwendung eines Nullbytes (\0) anstelle eines Zeilenvorschubzeichens (\n) stellt sicher, dass die Ausgabe von find weiterhin verständlich ist, falls einer der Dateinamen ein Zeilenvorschubzeichen enthält.

Mit der Option -z interpretieren sowohl sort als auch grep nur Nullbytes als Zeilenendezeichen. Da es keinen solchen Schalter für head gibt, verwenden wir stattdessen grep -m 1 (nur ein Vorkommen).

Die Befehle sind nach Ausführungszeit geordnet (gemessen auf meinem Computer).

  • Der erste Befehl ist der langsamste, da er die Zeit jeder Datei zuerst in ein für Menschen lesbares Format konvertieren und dann diese Zeichenfolgen sortieren muss. Durch das Weiterleiten an cat wird das Einfärben der Ausgabe vermieden.

  • Der zweite Befehl ist etwas schneller. Während die Datumskonvertierung noch ausgeführt wird, werden die seit Unix Epoch verstrichenen Sekunden etwas schneller numerisch sortiert (sort -n). sed löscht die Sekunden seit der Unix-Epoche.

  • Der letzte Befehl führt überhaupt keine Konvertierung durch und sollte deutlich schneller sein als die ersten beiden. Der Befehl find selbst zeigt nicht die Uhrzeit der ältesten Datei an, daher ist stat erforderlich.

Verwandte Manpages: find - grep - sed - sort - stat

11
Dennis

Obwohl die akzeptierte Antwort und andere hier die Arbeit erledigen, werden bei einem sehr großen Baum alle Dateien sortiert.

Besser wäre es, wenn wir sie einfach auflisten und den Überblick über die ältesten behalten könnten, ohne überhaupt sortieren zu müssen.

Deshalb habe ich mir diese alternative Lösung ausgedacht:

ls -lRU $PWD/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($1,0,1)=="/") { pat=substr($1,0,length($0)-1)"/"; }; if( $6 != "") {if ( $6 < oldd ) { oldd=$6; oldf=pat$8; }; print $6, pat$8; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

Ich hoffe, es könnte hilfreich sein, auch wenn die Frage ein bisschen alt ist.


Edit 1: Diese Änderungen ermöglichen das Parsen von Dateien und Verzeichnissen mit Leerzeichen. Es ist schnell genug, um es im Root / auszugeben und die älteste Datei zu finden, die es je gab.

ls -lRU --time-style=long-iso "$PWD"/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($0,0,1)=="/") { pat=substr($0,0,length($0)-1)"/"; $6="" }; if( $6 ~ /^[0-9]+$/) {if ( $6 < oldd ) { oldd=$6; oldf=$8; for(i=9; i<=NF; i++) oldf=oldf $i; oldf=pat oldf; }; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

Befehl erklärt:

  • ls -lRU --time-style = long-iso "$ PWD"/* listet alle Dateien (*) im Langformat (l) rekursiv (R) auf, ohne dass die Sortierung (U) schnell sein muss, und leitet sie an awk weiter
  • Awk, dann BEGINNEN, indem Sie den Zähler auf Null setzen (optional für diese Frage) und das älteste Datum auf Heute setzen. Formatieren Sie YearMonthDay.
  • Die Hauptschleife zuerst
    • Ergreift das 6. Feld, das Datum, formatiert Jahr-Monat-Tag und ändert es in YearMonthDay (wenn Ihr ls nicht auf diese Weise ausgibt, müssen Sie es möglicherweise fein abstimmen).
    • Bei Verwendung von rekursiv werden für alle Verzeichnisse Kopfzeilen in Form von/directory/here: angezeigt. Nehmen Sie diese Zeile in die Variable pat auf. (Ersetzen des letzten ":" durch ein "/"). Und setzt $ 6 auf nichts, um zu vermeiden, dass die Kopfzeile als gültige Dateizeile verwendet wird.
    • wenn das Feld $ 6 eine gültige Nummer hat, ist es ein Datum. Vergleichen Sie es mit dem alten Datum oldd.
    • Ist es älter Speichern Sie dann die neuen Werte für das alte Datum oldd und den alten Dateinamen oldf. Übrigens ist oldf nicht nur das 8. Feld, sondern vom 8. bis zum Ende. Das ist der Grund, warum eine Schleife vom 8. zum NF (Ende) verkettet werden soll.
    • Zähle die Fortschritte um eins
    • ENDE durch Drucken des Ergebnisses

Laufen es:

~ $ time ls -lRU "$ PWD"/* | awk etc.

Ältestes Datum: 19691231

Datei: /home/.../.../backupold/.../EXAMPLES/how-to-program.txt

Insgesamt verglichen: 111438

echte 0m1.135s

benutzer 0m0.872s

sys 0m0.760s


EDIT 2: Gleiches Konzept, bessere Lösung mit find zum Anzeigen der Zugriffszeit (verwenden Sie %T mit der ersten printf für Änderungszeit oder %C für Statusänderung stattdessen).

find . -wholename "*" -type f -printf "%AY%Am%Ad %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

EDIT 3: Der folgende Befehl verwendet Änderungszeit und gibt auch den inkrementellen Fortschritt aus, wenn ältere und ältere Dateien gefunden werden. Dies ist hilfreich, wenn Sie falsche Zeitstempel haben (z. B. 1970-01-01):

find . -wholename "*" -type f -printf "%TY%Tm%Td %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; print oldd " " oldf; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
5
Dr Beco

Bitte benutzen Sie ls - die Manpage zeigt Ihnen, wie Sie das Verzeichnis bestellen können.

ls -clt | head -n 2

Das -n 2 ist so, dass Sie nicht die "Summe" in der Ausgabe erhalten. Wenn Sie nur den Namen der Datei wollen.

ls -t | head -n 1

Und wenn Sie die Liste in der normalen Reihenfolge benötigen (immer die neueste Datei)

ls -tr | head -n 1

Viel einfacher als die Verwendung von find, viel schneller und robuster - Sie müssen sich keine Gedanken über Dateibenennungsformate machen. Es sollte auch auf fast allen Systemen funktionieren.

4
user1363990
find ! -type d -printf "%[email protected] %p\n" | sort -n | head -n1
2
Okki

Es scheint, dass die meisten Leute unter "ältesten" angenommen haben, dass Sie "älteste Änderungszeit" gemeint haben. Das ist wahrscheinlich nach der strengsten Interpretation von "ältesten" korrigiert, aber falls Sie die mit der ältesten Zugriffs Zeit wollten, würde ich die beste Antwort folgendermaßen modifizieren:

find -type f -printf '%A+ %p\n' | sort | head -n 1

Beachten Sie den %A+.

0
PenguinLust