Ich suche einen Shell-Einzeiler, um die älteste Datei in einem Verzeichnisbaum zu finden.
Ich suche einen Shell-Einzeiler, um die älteste Datei in einem Verzeichnisbaum zu finden.
Antworten:
Dies funktioniert (aktualisiert, um den Vorschlag von Daniel Andersson zu berücksichtigen):
find -type f -printf '%T+ %p\n' | sort | head -n 1
find
leer ist, weil der Dateiname eine neue Zeile enthält.
Dies ist etwas portabler und weil es nicht auf der GNU- find
Erweiterung basiert -printf
, 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 beschränkt ist ARG_MAX
(was 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, weil -print0
und xargs -0
nicht.
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
xargs: ls: terminated by signal 13
Fehler als Nebeneffekt ausgegeben. Ich vermute, das ist SIGPIPE. Ich habe keine Ahnung, warum ich keinen ähnlichen Fehler erhalte, wenn ich die Ausgabe von sort in meine Lösung pipe.
head
Befehl, der beendet wird, sobald er eine Zeile gelesen hat und somit die Pipe "durchbricht", denke ich. Sie erhalten den Fehler nicht, weil er sort
sich nicht zu beschweren ls
scheint, im anderen Fall jedoch.
xargs
die ls
mehr als einmal aufgerufen werden müssen. In diesem Fall werden die sortierten Ausgaben dieser mehreren Aufrufe verkettet, wenn sie zusammengeführt werden sollen.
ls
es und die älteste Datei, Ihre Lösung Augapfel wahrscheinlich wird die Kommandozeile Längenbegrenzung überrannt, was ls
mehrfach aufgerufen werden. Sie werden die falsche Antwort bekommen, aber Sie werden es nie erfahren.
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 "%T@ %T+ %p\0" | \
sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
stat -c "%y %n" "$(find -type f -printf "%T@ %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.
Der -z
Schalter bewirkt, dass sowohl sort als auch grep nur Nullbytes als Zeilenendezeichen interpretieren. Da es keinen solchen Schalter für den Kopf gibt, verwenden wir grep -m 1
stattdessen (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, ist die numerische Sortierung ( sort -n
) der seit der Unix-Epoche verstrichenen Sekunden etwas schneller. 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.
Obwohl die akzeptierte Antwort und andere hier die Aufgabe erfüllen, 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 sie zu sortieren.
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.
Bearbeiten 1: Diese Änderungen ermöglichen das Parsen von Dateien und Verzeichnissen mit Leerzeichen. Es ist schnell genug, um es im Stammverzeichnis 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:
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
Blick auf die Zugriffszeit ( %T
mit der ersten printf
für Änderungszeit oder stattdessen %C
für Statusänderung ).
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}'
BEARBEITEN 3: Der folgende Befehl verwendet die Änderungszeit und gibt auch den inkrementellen Fortschritt aus, wenn ältere und ältere Dateien gefunden werden. Dies ist hilfreich, wenn Sie falsche Zeitstempel haben (wie zum Beispiel 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}'
ls
ist für Skripte ungeeignet, da die Ausgabe nicht für Computer bestimmt ist. Die Ausgabeformatierung variiert je nach Implementierung. Wie Sie bereits sagten, find
ist es gut für Skripte, aber es kann auch sinnvoll sein, diese Informationen hinzuzufügen, bevor Sie über ls
Lösungen sprechen .
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.
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
sort -n
.
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 Zugriffszeit wollten , würde ich die beste Antwort folgendermaßen modifizieren:
find -type f -printf '%A+ %p\n' | sort | head -n 1
Beachten Sie die %A+
.
set $(find /search/dirname -type f -printf '%T+ %h/%f\n' | sort | head -n 1) && echo $2
find ./search/dirname -type f -printf '%T+ %h/%f\n'
druckt Datums- und Dateinamen in zwei Spalten.sort | head -n1
behält die Zeile bei, die der ältesten Datei entspricht.echo $2
zeigt die zweite Spalte an, dh den Dateinamen.
find -type f -printf '%T+ %p\n' | sort | head -1