Wie finde und liste ich die zuletzt geänderten Dateien rekursiv in einem Verzeichnis mit Unterverzeichnissen und Zeiten auf?

417
  • Betriebssystem: Linux

  • Dateisystemtyp: ext3

  • Bevorzugte Lösung: Bash (Skript / Oneliner), Ruby, Python

Ich habe mehrere Verzeichnisse mit mehreren Unterverzeichnissen und Dateien darin. Ich muss eine Liste aller dieser Verzeichnisse erstellen, die so aufgebaut ist, dass jedes Verzeichnis der ersten Ebene neben dem Datum und der Uhrzeit der zuletzt erstellten / geänderten Datei darin aufgeführt ist.

Wenn ich eine Datei berühre oder ihren Inhalt einige Unterverzeichnisebenen später ändere, sollte dieser Zeitstempel neben dem Verzeichnisnamen der ersten Ebene angezeigt werden. Angenommen, ich habe ein Verzeichnis, das wie folgt aufgebaut ist:

./alfa/beta/gamma/example.txt

und ich ändere den Inhalt der Datei example.txt, ich brauche diese Zeit neben dem Verzeichnis der ersten Ebene alfain lesbarer Form angezeigt , nicht Epoche. Ich habe einige Dinge mit find und ähnlichen Methoden ausprobiert xargs, sortaber ich kann das Problem nicht umgehen, dass sich der Zeitstempel des Dateisystems von 'alfa' nicht ändert, wenn ich Dateien ein paar Ebenen tiefer erstelle / ändere.

fredrik
quelle
Wenn Sie die Mühe des Bauens ertragen können , kann github.com/shadkam/recentmost verwendet werden.
user3392225
4
Unglaublich. 16 Antworten, und die meisten / alle versuchen nicht einmal, das zu tun, was das OP angegeben hat ...
hmijail trauert um die Rücktrittsberechtigten
Anstelle von Lösungen wie einem -R-Schalter sehe ich hier nur Masse.
NeverMind9
Sollte eine native Funktion sein.
NeverMind9

Antworten:

486

Probier diese:

#!/bin/bash
find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head

Führen Sie es mit dem Pfad zu dem Verzeichnis aus, in dem das rekursive Scannen beginnen soll (es unterstützt Dateinamen mit Leerzeichen).

Wenn viele Dateien vorhanden sind, kann es eine Weile dauern, bis etwas zurückgegeben wird. Die Leistung kann verbessert werden, wenn wir xargsstattdessen Folgendes verwenden :

#!/bin/bash
find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head

das ist ein bisschen schneller.

Heppo
quelle
132
Ihre "schnelle Methode" sollte auch print0 verwenden können, um Leerzeichen und sogar Zeilenvorschübe in Dateinamen zu unterstützen. Folgendes verwende ich: find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head Das schafft es immer noch, schnell für mich zu sein.
Dan
20
Unter Mac OS X ist es nicht der Status von GNU, daher schlägt der Befehl fehl. Sie müssen brew install coreutilsund verwenden gstatstattstat
CharlesB
36
Sie müssen nicht laufen, statda find PATH -type f -printf "%T@ %p\n"| sort -nrder Job erledigt ist. Auf diese Weise geht es auch etwas schneller.
Nr.
4
Können wir den Kommentar von @ user37078 irgendwie in eine tatsächliche Antwort verwandeln oder die ursprüngliche Antwort bearbeiten? es scheint "der richtige Weg" zu sein, es zu tun.
Mnagel
6
Unter Mac OS X können Sie ohne Installation von gstat oder etwas anderem find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
Folgendes
198

So finden Sie alle Dateien, deren Dateistatus zuletzt vor N Minuten geändert wurde:

find -cmin -N

zum Beispiel:

find -cmin -5

Iman
quelle
4
+1 Danke, sehr nützlich. Funktioniert unter Windows mit GnuWin32 find.
Sabuncu
sehr knapp. Sehr schön!
Randy L
Es ist schneller als andere Lösungen komplizierter
david.perez
20
Wirklich gut, auch Sie können 'find -ctime -50' zum Beispiel für die letzten 50 Tage der Änderung verwenden.
Gorkem
1
Verwenden Siesudo find -cmin -1 2>&1 |grep -v /proc/
Cees Timmerman
39

GNU Find (siehe man find) verfügt über einen -printfParameter zum Anzeigen der EPOC-Zeit und des relativen Pfadnamens.

redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'
user2570243
quelle
3
Vielen Dank! Dies ist die einzige Antwort, die schnell genug ist, um meine sehr breite Verzeichnisstruktur in angemessener Zeit zu durchsuchen. Ich leite die Ausgabe durch tail, um zu verhindern, dass Tausende von Zeilen in der Ausgabe gedruckt werden.
sffc
8
Ein weiterer Kommentar: Der awk '{print $2}'Teil scheint Probleme zu verursachen, wenn Dateinamen mit Leerzeichen vorhanden sind. Hier ist eine Lösung, die sedstattdessen verwendet, und es druckt auch die Zeit zusätzlich zum Pfad:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc
3
Ich denke, es sollte sort -rn sein
Bojan Dević
2
Die -printf-Variante ist viel schneller als jedes Mal einen 'stat'-Prozess aufzurufen - sie spart Stunden bei meinen Sicherungsjobs. Vielen Dank, dass Sie mich darauf aufmerksam gemacht haben. Ich habe die awk / sed-Sache vermieden, da ich nur über das letzte Update innerhalb des Baums besorgt bin - also X = $ (find / path-Typ f -printf '% T% p \ n' | grep -v etwas-I- don-tcare-about | sort -nr | head -n 1) und ein Echo $ {X # * ""} funktionierten gut für mich (gib mir Sachen bis zum ersten Leerzeichen)
David Goodwin
2
Alles funktioniert nicht, wenn der Dateiname mehrzeilig ist. Verwenden Sie touch "lala<Enter>b"diese Option , um eine solche Datei zu erstellen. Ich denke, Unix Utilities Design hat große Fehler in Bezug auf Dateinamen.
Obst
35

Ich habe die großartige Antwort von Halo auf diesen Einzeiler verkürzt

stat --printf="%y %n\n" $(ls -tr $(find * -type f))

Aktualisiert : Wenn die Dateinamen Leerzeichen enthalten, können Sie diese Änderung verwenden

OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";
Slashdottir
quelle
Wie wäre es damit: IFS = $ '\ n'; stat --printf = "% y% n \ n" $ (ls -tr $ (find. -Typ f))
slashdottir
3
Dies funktioniert nicht, wenn Sie eine sehr große Anzahl von Dateien haben. Die Antworten, die xargs verwenden, lösen diese Grenze.
Carl Verbiest
@ carlverbiest in der Tat wird eine große Anzahl von Dateien die Lösung von slashdottir brechen. Sogar xargs-basierte Lösungen werden dann langsam sein. Die Lösung von user2570243 eignet sich am besten für große Dateisysteme.
Stéphane Gourichon
IFS=$'\n'ist auf keinen Fall sicher beim Umgang mit Dateinamen: Zeilenumbrüche sind gültige Zeichen in Dateinamen unter UNIX. Nur das NUL-Zeichen ist garantiert nicht in einem Pfad vorhanden.
Charles Duffy
17

Versuche dies

#!/bin/bash
stat --format %y $(ls -t $(find alfa/ -type f) | head -n 1)

Es wird verwendet find, um alle Dateien aus dem Verzeichnis zu sammeln, lssie nach Änderungsdatum sortiert headaufzulisten, die erste Datei auszuwählen und schließlich statdie Uhrzeit in einem schönen Format anzuzeigen.

Derzeit ist es nicht sicher für Dateien mit Leerzeichen oder anderen Sonderzeichen im Namen. Schreiben Sie eine Empfehlung, wenn sie Ihren Anforderungen noch nicht entspricht.

Daniel Böhmer
quelle
1
halo: Ich mag deine Antwort, sie funktioniert gut und druckt die richtige Datei aus. Ich helfe mir jedoch nicht, da es in meinem Fall zu viele Unterebenen gibt. Also bekomme ich "Argumentliste zu lang" für ls ... und xargs würde auch in diesem Fall nicht helfen. Ich werde etwas anderes versuchen.
Fredrik
In diesem Fall ist es etwas komplexer und benötigt ein echtes Programm. Ich werde etwas Perl hacken.
Daniel Böhmer
1
Ich habe dies stattdessen mit PHP gelöst. Eine rekursive Funktion, die durch den Dateisystembaum absteigt und die Zeit der zuletzt geänderten Datei speichert.
Fredrik
11

Dieser Befehl funktioniert unter Mac OS X:

find "$1" -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head

Verwenden Sie unter Linux, wie im Originalposter angegeben, statanstelle von gstat.

Diese Antwort ist natürlich die herausragende Lösung von user37078 , die vom Kommentar zur vollständigen Antwort befördert wird. Ich habe CharlesBs Erkenntnisse für gstatMac OS X eingemischt. Ich habe übrigens eher Coreutils von MacPorts als Homebrew erhalten .

Und so habe ich das in einen einfachen Befehl ~/bin/ls-recent.shzur Wiederverwendung gepackt:

#!/bin/bash
# ls-recent: list files in a dir tree, most recently modified first
#
# Usage: ls-recent path [-10 | more]
# 
# Where "path" is a path to target directory, "-10" is any arg to pass
# to "head" to limit the number of entries, and "more" is a special arg
# in place of "-10" which calls the pager "more" instead of "head".
if [ "more" = "$2" ]; then
   H=more; N=''
else
   H=head; N=$2
fi

find "$1" -type f -print0 |xargs -0 gstat --format '%Y :%y %n' \
    |sort -nr |cut -d: -f2- |$H $N
Jim DeLaHunt
quelle
2
Unter OS X Yosemite; Ich erhalte die Fehlermeldung: find: ftsopen: Keine solche Datei oder kein solches Verzeichnis
Reece
Interessant. Welchen Befehl haben Sie eingegeben (mit Parametern)? Und wie hießen die Dateien in diesem Verzeichnis? Und wenn Sie Ihre eigene Version von erstellt haben ~/bin/ls-recent.sh, haben Sie das Skript sorgfältig auf Unterschiede überprüft?
Jim DeLaHunt
10
für diejenigen, die nichts unter Mac OS X installieren möchten:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake
5

Sowohl die Perl- als auch die Python-Lösung in diesem Beitrag haben mir geholfen, dieses Problem unter Mac OS X zu lösen: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively -no-stat-befehlsverfügbar .

Zitat aus dem Beitrag:

Perl:

find . -type f -print |
perl -l -ne '
    $_{$_} = -M;  # store file age (mtime - now)
    END {
        $,="\n";
        print sort {$_{$b} <=> $_{$a}} keys %_;  # print by decreasing age
    }'

Python:

find . -type f -print |
python -c 'import os, sys; times = {}
for f in sys.stdin.readlines(): f = f[0:-1]; times[f] = os.stat(f).st_mtime
for f in sorted(times.iterkeys(), key=lambda f:times[f]): print f'
William Niu
quelle
5

Versteckte Dateien ignorieren - mit schönem und schnellem Zeitstempel

Behandelt Leerzeichen in Dateinamen gut - nicht, dass Sie diese verwenden sollten!

$ find . -type f -not -path '*/\.*' -printf '%TY.%Tm.%Td %THh%TM %Ta %p\n' |sort -nr |head -n 10

2017.01.28 07h00 Sat ./recent
2017.01.21 10h49 Sat ./hgb
2017.01.16 07h44 Mon ./swx
2017.01.10 18h24 Tue ./update-stations
2017.01.09 10h38 Mon ./stations.json

Weitere findInformationen finden Sie unter dem Link.

Serge Stroobandt
quelle
3

Ich zeige dies für die letzte Zugriffszeit. Sie können dies leicht ändern, um die neueste Mod-Zeit zu erhalten.

Es gibt zwei Möglichkeiten, dies zu tun:


1) Wenn Sie eine globale Sortierung vermeiden möchten, die bei mehreren zehn Millionen Dateien teuer sein kann, können Sie Folgendes tun: (Positionieren Sie sich im Stammverzeichnis des Verzeichnisses, in dem Ihre Suche beginnen soll.)

linux> touch -d @0 /tmp/a;
linux> find . -type f -exec tcsh -f -c test `stat --printf="%X" {}` -gt  `stat --printf="%X" /tmp/a`  ; -exec tcsh -f -c touch -a -r {} /tmp/a ; -print 

Die obige Methode druckt Dateinamen mit immer neuerer Zugriffszeit und die letzte Datei, die gedruckt wird, ist die Datei mit der neuesten Zugriffszeit. Sie können natürlich die neueste Zugriffszeit mit einem "Schwanz -1" erhalten.


2) Sie können den Namen und die Zugriffszeit aller Dateien in Ihrem Unterverzeichnis rekursiv drucken lassen und dann nach Zugriffszeit und dem Ende des größten Eintrags sortieren:

linux> \find . -type f -exec stat --printf="%X  %n\n" {} \; | \sort -n | tail -1

Und da hast du es ...

Sean
quelle
3

Ich habe diesen Alias ​​in meinem .profile, den ich ziemlich oft benutze

$ alias | grep xlogs
xlogs='sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R'

Es macht also das, wonach Sie suchen (mit Ausnahme von Datums- und Uhrzeitänderungen auf mehreren Ebenen) - sucht nach den neuesten Dateien (in diesem Fall * .log- und * .trc-Dateien); Außerdem werden nur Dateien gefunden, die am letzten Tag geändert wurden. Anschließend werden sie nach Zeit sortiert und über weniger ausgegeben:

sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R

ps. Beachten Sie, dass ich auf einigen Servern kein Root-Verzeichnis habe, aber immer sudo, sodass Sie diesen Teil möglicherweise nicht benötigen.

Tagar
quelle
Wie ist das "genau das, wonach Sie suchen"? Das OP hat eine gute Erklärung geschrieben, was er wollte, und dies ignoriert es völlig.
Hmijail trauert um die Rücktrittsberechtigten
Danke, dass du darauf hingewiesen hast. Sie haben Recht - diese Methode führt nicht über mehrere Ebenen, um Änderungsdatum / -zeit zu erhalten, sondern zeigt nur Datum / Uhrzeit der darin enthaltenen Verzeichnisdateien an. habe meine Antwort bearbeitet.
Tagar
1

Sie können den Befehl printf geben, um einen Versuch zu finden

% Ak @' or a directive for the C Files letzte Zugriffszeit in dem durch k angegebenen Format, das entweder die Strftime-Funktion ist. Die möglichen Werte für k sind unten aufgeführt; Einige von ihnen sind möglicherweise nicht auf allen Systemen verfügbar, da sich die Zeit zwischen den Systemen unterscheidet.

Graugans
quelle
1

Schnelle Bash-Funktion:

# findLatestModifiedFiles(directory, [max=10, [format="%Td %Tb %TY, %TT"]])
function findLatestModifiedFiles() {
    local d="${1:-.}"
    local m="${2:-10}"
    local f="${3:-%Td %Tb %TY, %TT}"

    find "$d" -type f -printf "%T@ :$f %p\n" | sort -nr | cut -d: -f2- | head -n"$m"
}

Suchen Sie die zuletzt geänderte Datei in einem Verzeichnis:

findLatestModifiedFiles "/home/jason/" 1

Sie können auch Ihr eigenes Datums- / Uhrzeitformat als drittes Argument angeben.

Jason Larke
quelle
1

Im Folgenden erhalten Sie eine Zeichenfolge des Zeitstempels und den Namen der Datei mit dem neuesten Zeitstempel:

find $Directory -type f -printf "%TY-%Tm-%Td-%TH-%TM-%TS %p\n" | sed -r 's/([[:digit:]]{2})\.([[:digit:]]{2,})/\1-\2/' |     sort --field-separator='-' -nrk1 -nrk2 -nrk3 -nrk4 -nrk5 -nrk6 -nrk7 | head -n 1

Ergebnis einer Ausgabe des Formulars: <yy-mm-dd-hh-mm-ss.nanosec> <filename>

mark_infinite
quelle
1

Hier ist eine Version, die mit Dateinamen arbeitet, die auch Leerzeichen, Zeilenumbrüche und Glob-Zeichen enthalten können:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printfDruckt eine Dateimodifikation (EPOCH-Wert), gefolgt von einem Leerzeichen und \0abgeschlossenen Dateinamen.
  • sort -zk1nr liest NUL-terminierte Daten und sortiert sie umgekehrt numerisch

Da die Frage mit Linux markiert ist, gehe ich davon aus, dass gnuUtils verfügbar sind.

Sie können oben mit folgenden Pipe:

xargs -0 printf "%s\n"

zum Drucken der Änderungszeit und der Dateinamen, sortiert nach der Änderungszeit (zuletzt zuerst), die durch Zeilenumbrüche beendet wird.

Anubhava
quelle
1

Folgendes verwende ich (sehr effizient):

function find_last () { find "${1:-.}" -type f -printf '%TY-%Tm-%Td %TH:%TM %P\n' 2>/dev/null | sort | tail -n "${2:-10}" }

PROS:

  • Es werden nur 3 Prozesse erzeugt

VERWENDUNGSZWECK:

find_last [dir [number]]

wo:

  • dir - ein zu durchsuchendes Verzeichnis [aktuelles Verzeichnis]
  • number - Anzahl der neuesten anzuzeigenden Dateien [10]

Die Ausgabe für find_last /etc 4sieht folgendermaßen aus:

2019-07-09 12:12 cups/printers.conf
2019-07-09 14:20 salt/minion.d/_schedule.conf
2019-07-09 14:31 network/interfaces
2019-07-09 14:41 environment
Seweryn Niemiec
quelle
0

lsVerwenden Sie dies für eine einfache Ausgabe. Es gibt keine Argumentliste, daher kann es nicht zu lang werden:

find . | while read FILE;do ls -d -l "$FILE";done

Und nur mit cutDatum, Uhrzeit und Namen versehen:

find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5

BEARBEITEN : Ich habe gerade bemerkt, dass die aktuelle Top-Antwort nach Änderungsdatum sortiert ist. Das ist mit dem zweiten Beispiel hier genauso einfach, da das Änderungsdatum in jeder Zeile an erster Stelle steht - schlagen Sie eine Sortierung auf das Ende:

find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5 | sort
Izkata
quelle
0

Dies könnte auch mit einer rekursiven Funktion in bash erfolgen

Sei F eine Funktion, die die Zeit der Datei anzeigt, die lexikographisch sortierbar sein muss JJJJ-MM-TT usw. (os-abhängig?)

F(){ stat --format %y "$1";}                # Linux
F(){ ls -E "$1"|awk '{print$6" "$7}';}      # SunOS: maybe this could be done easier

R die rekursive Funktion, die Verzeichnisse durchläuft

R(){ local f;for f in "$1"/*;do [ -d "$f" ]&&R $f||F "$f";done;}

Und schlussendlich

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
Nahuel Fouilleul
quelle