Jak rekurencyjnie znajdować i wyświetlać najnowsze zmodyfikowane pliki w katalogu z podkatalogami i godzinami?

417
  • System operacyjny: Linux

  • Typ systemu plików: ext3

  • Preferowane rozwiązanie: bash (skrypt / oneliner), ruby, python

Mam kilka katalogów z kilkoma podkatalogami i plikami. Muszę sporządzić listę wszystkich tych katalogów, która jest zbudowana w taki sposób, że każdy katalog pierwszego poziomu jest wyświetlany obok daty i godziny najnowszego utworzonego / zmodyfikowanego pliku w nim.

Aby wyjaśnić, jeśli dotknę pliku lub zmodyfikuję jego zawartość o kilka poziomów podkatalogów w dół, znacznik czasu powinien być wyświetlany obok nazwy katalogu pierwszego poziomu. Powiedzmy, że mam katalog o takiej strukturze:

./alfa/beta/gamma/example.txt

i modyfikuję zawartość pliku example.txt, potrzebuję czasu wyświetlanego obok katalogu pierwszego poziomu alfaw czytelnej dla człowieka formie, a nie epoce. Próbowałem kilka rzeczy za pomocą znalezienia xargs, sorti lubi, ale nie mogę obejść ten problem, że timestamp plików z „alfa” nie zmienia się podczas tworzenia / modyfikowania plików kilka poziomów w dół.

Fredrik
źródło
Jeśli możesz zaryzykować zbudowanie go, możesz użyć github.com/shadkam/recentmost .
user3392225,
4
Niesamowite. 16 odpowiedzi, a większość / wszyscy nawet nie próbują robić tego, co określił PO ...
hmijail opłakuje
Zamiast rozwiązań takich jak przełącznik -R, po prostu widzę tutaj masę.
neverMind9,
Powinien być funkcją natywną.
neverMind9,

Odpowiedzi:

486

Spróbuj tego:

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

Uruchom go ze ścieżką do katalogu, w którym powinien rozpocząć skanowanie rekurencyjnie (obsługuje nazwy plików ze spacjami).

Jeśli jest dużo plików, może to chwilę potrwać, zanim cokolwiek zwróci. Wydajność można poprawić, jeśli xargszamiast tego zastosujemy :

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

co jest nieco szybsze.

Heppo
źródło
132
Twoja „szybka metoda” powinna także umożliwiać użycie print0 do obsługi spacji, a nawet linii w nazwach plików. Oto, czego używam: find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head to wciąż jest dla mnie szybkie.
Dan
20
W Mac OS X nie jest to statystyka GNU, więc polecenie nie działa. Musisz brew install coreutilsi użyj gstatzamiaststat
CharlesB
36
Nie musisz biegać, statponieważ find PATH -type f -printf "%T@ %p\n"| sort -nrwykonuje zadanie. W ten sposób jest też nieco szybszy.
nr
4
czy możemy w jakiś sposób zmienić komentarz @ user37078 w rzeczywistą odpowiedź lub edytować oryginalną odpowiedź? wydaje się to być „właściwą drogą” [tm], aby to zrobić.
mnagel
6
W systemie Mac OS X, bez instalowania programu gstat lub czegokolwiek innego, możesz:find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
cobbzilla
198

Aby znaleźć wszystkie pliki, których status został ostatnio zmieniony N minut temu:

find -cmin -N

na przykład:

find -cmin -5

iman
źródło
4
+1 Dzięki, bardzo przydatne. Działa w systemie Windows przy użyciu GnuWin32 find.
Sabuncu
bardzo zwięzłe. bardzo dobrze!
Randy L
jest szybszy niż inne rozwiązania bardziej skomplikowane
david.perez
20
Naprawdę dobrze, możesz także użyć „find -ctime -50” na przykład dla ostatnich 50 dni zmiany.
Gorkem
1
Aby wykluczyć bałagan, użyjsudo find -cmin -1 2>&1 |grep -v /proc/
Cees Timmerman
39

GNU Find (patrz man find) ma -printfparametr do rozproszenia plików EPOC mtime i względnej nazwy ścieżki.

redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'
użytkownik2570243
źródło
3
Dzięki! To jedyna odpowiedź, która jest wystarczająco szybka, aby przeszukać moją bardzo szeroką strukturę katalogów w rozsądnym czasie. Przepuszczam dane wyjściowe, tailaby zapobiec wydrukowaniu tysięcy linii na wydruku.
sffc 18.10.13
8
Kolejny komentarz: awk '{print $2}'część wydaje się powodować problemy, gdy istnieją nazwy plików ze spacjami. Oto rozwiązanie wykorzystujące sedzamiast tego, a także wyświetla czas oprócz ścieżki:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc 18.10.13
3
Myślę, że powinien to być rodzaj -rn
Bojan Dević
2
Wariant -printf jest o wiele szybszy niż wywoływanie procesu „stat” za każdym razem - skraca godziny pracy moich zadań tworzenia kopii zapasowych. Dzięki za poinformowanie mnie o tym. Unikałem rzeczy awk / sed, ponieważ martwię się tylko o ostatnią aktualizację w drzewie - więc X = $ (find / path -type f -printf '% T% p \ n' | grep -v coś-I- don-tcare-about | sort -nr | head -n 1) i echo $ {X # * ""} działało dla mnie dobrze (daj mi rzeczy do pierwszego miejsca)
David Goodwin
2
Nie wszystko będzie działać, jeśli nazwa pliku w wielu wierszach. Użyj, touch "lala<Enter>b"aby utworzyć taki plik. Myślę, że projektowanie narzędzi unix ma dużą wadę w nazwie pliku.
Fruit
35

Skróciłem niesamowitą odpowiedź halo na ten jednowarstwowy

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

Zaktualizowano : Jeśli w nazwach plików znajdują się spacje, możesz użyć tej modyfikacji

OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";
slashdottir
źródło
Co powiesz na to: IFS = $ '\ n'; stat --printf = "% y% n \ n" $ (ls -tr $ (find. -type f))
slashdottir
3
To nie zadziała, jeśli masz bardzo dużą liczbę plików. odpowiedzi, które używają xargs rozwiązują ten limit.
carl verbiest
@ carlverbiest rzeczywiście duża liczba plików złamie rozwiązanie slashdottir. Nawet rozwiązania oparte na xargs będą wtedy działać wolno. Rozwiązanie user2570243 jest najlepsze dla dużych systemów plików.
Stéphane Gourichon
IFS=$'\n'w żadnym wypadku nie jest bezpieczne podczas obsługi nazw plików: znaki nowej linii to prawidłowe znaki w nazwach plików w systemie UNIX. Tylko znak NUL gwarantuje, że nie będzie obecny na ścieżce.
Charles Duffy,
17

Spróbuj tego

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

Służy finddo gromadzenia wszystkich plików z katalogu, lswyświetlania ich posortowanych według daty modyfikacji, headwybierania pierwszego pliku i wreszcie statwyświetlania czasu w ładnym formacie.

W tej chwili nie jest bezpieczne dla plików z białymi znakami lub innymi znakami specjalnymi w ich nazwach. Napisz pochwałę, jeśli jeszcze nie spełnia twoich potrzeb.

Daniel Böhmer
źródło
1
halo: Podoba mi się twoja odpowiedź, działa dobrze i drukuje poprawny plik. Nie pomagam jednak, ponieważ w moim przypadku jest za dużo podpoziomów. Dostaję więc „listę argumentów za długą” dla ls ... i xargs też nie pomógłby w tym przypadku. Spróbuję czegoś innego.
fredrik
W takim przypadku jest to nieco bardziej złożone i będzie wymagało prawdziwego programu. Zhakuję trochę Perla.
Daniel Böhmer,
1
Rozwiązałem to za pomocą PHP. Funkcja rekurencyjna, która opuszcza drzewo systemu plików i przechowuje czas ostatnio zmodyfikowanego pliku.
fredrik
11

To polecenie działa w systemie Mac OS X:

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

W systemie Linux, zgodnie z pytaniem oryginalnego plakatu, użyj statzamiast gstat.

Ta odpowiedź jest oczywiście znakomitym rozwiązaniem użytkownika user37078 , awansowanym od komentarza do pełnej odpowiedzi. I mieszać w CharlesB wglądu „s do użytku gstatw systemie Mac OS X. Dostałem Coreutils z DarwinPorts zamiast homebrew , nawiasem mówiąc.

A oto jak spakowałem to w proste polecenie ~/bin/ls-recent.shdo ponownego użycia:

#!/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
źródło
2
W systemie OS X yosemite; Pojawia się błąd: find: ftsopen: Brak takiego pliku lub katalogu
Reece
Ciekawy. Jakie polecenie wpisałeś (z parametrami)? A jakie były nazwy plików w tym katalogu? A jeśli stworzyłeś własną wersję ~/bin/ls-recent.sh, czy dokładnie sprawdziłeś skrypt pod kątem różnic?
Jim DeLaHunt,
10
dla tych, którzy nie chcą instalować niczego na Mac OS X:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake
5

Zarówno rozwiązania Perla, jak i Pythona w tym poście pomogły mi rozwiązać ten problem w systemie Mac OS X: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively -no-stat-polecenie-dostępne .

Cytowanie z postu:

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
    }'

Pyton:

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
źródło
5

Ignorowanie ukrytych plików - z ładnym i szybkim znacznikiem czasu

Dobrze radzi sobie ze spacjami w nazwach plików - nie żebyś ich używał!

$ 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

Więcej findobfitości można znaleźć, klikając link.

Serge Stroobandt
źródło
3

Pokazuję to dla ostatniego czasu dostępu, możesz łatwo to zmodyfikować, aby zrobić najnowszy czas modyfikacji.

Można to zrobić na dwa sposoby:


1) Jeśli chcesz uniknąć globalnego sortowania, które może być kosztowne, jeśli masz dziesiątki milionów plików, możesz: (ustawić się w katalogu głównym katalogu, w którym chcesz rozpocząć wyszukiwanie)

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 

Powyższa metoda drukuje nazwy plików z coraz nowszym czasem dostępu, a ostatnim drukowanym plikiem jest plik z najnowszym czasem dostępu. Możesz oczywiście uzyskać najnowszy czas dostępu, używając „ogona -1”.


2) Możesz znaleźć rekursywnie wydrukować nazwę, czas dostępu do wszystkich plików w podkatalogu, a następnie posortować według czasu dostępu i ogona największego wpisu:

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

I masz to ...

Sean
źródło
3

Mam ten alias w moim .profile, którego używam dość często

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

Robi to, czego szukasz (z wyjątkiem tego, że nie przemierza wielu poziomów daty / godziny) - szuka najnowszych plików (w tym przypadku plików * .log i * .trc); także wyszukuje tylko pliki zmodyfikowane w ciągu ostatniego dnia, a następnie sortuje według czasu i potoków generuje mniej:

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

ps. Zauważ, że nie mam root na niektórych serwerach, ale zawsze mam sudo, więc możesz nie potrzebować tej części.

Tagar
źródło
Jak to jest „dokładnie to, czego szukasz”? OP napisał dobre wyjaśnienie tego, czego chciał, a to całkowicie go ignoruje.
Hmijail opłakuje odrodzenie
dzięki za wskazanie na to. masz rację - ta metoda nie przechodzi na wiele poziomów, aby uzyskać datę / godzinę zmiany, pokazuje tylko datę / godzinę plików katalogów w jej obrębie. zredagowałem moją odpowiedź.
Tagar
1

Możesz wydać komendę printf, aby znaleźć próbę

% Czasu ostatniego dostępu do pliku Ak w formacie określonym przez k, który jest @' or a directive for the C funkcją strftime. Możliwe wartości dla k są wymienione poniżej; niektóre z nich mogą nie być dostępne we wszystkich systemach z powodu różnic w „strftime” między systemami.

graugans
źródło
1

Funkcja szybkiego uderzenia:

# 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"
}

Znajdź najnowszy zmodyfikowany plik w katalogu:

findLatestModifiedFiles "/home/jason/" 1

Możesz również określić swój własny format daty / godziny jako trzeci argument.

Jason Larke
źródło
1

Poniższe zwraca ciąg znacznika czasu i nazwę pliku z najnowszym znacznikiem czasu:

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

Wynikające z wyniku formularza: <yy-mm-dd-hh-mm-ss.nanosec> <filename>

mark_infinite
źródło
1

Oto jedna wersja, która działa z nazwami plików, które mogą również zawierać spacje, znaki nowej linii, znaki globalne:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printfwypisuje modyfikację pliku (wartość EPOCH), po której następuje spacja i \0zakończone nazwy plików.
  • sort -zk1nr odczytuje zakończone NUL dane i sortuje je odwrotnie numerycznie

Ponieważ pytanie jest oznaczone gnulinuksem, zakładam, że narzędzia są dostępne.

Możesz wykonać potok powyżej:

xargs -0 printf "%s\n"

aby wydrukować czas modyfikacji i nazwy plików posortowane według czasu modyfikacji (najnowsze od pierwszego) zakończonego znakiem nowej linii.

anubhava
źródło
1

Właśnie tego używam (bardzo wydajny):

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

Plusy:

  • spawnuje tylko 3 procesy

STOSOWANIE:

find_last [dir [number]]

gdzie:

  • dir - katalog do przeszukania [aktualny katalog]
  • number - liczba najnowszych plików do wyświetlenia [10]

Dane wyjściowe dla find_last /etc 4wygląda następująco:

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
źródło
0

Aby uzyskać zwykły lswydruk, użyj tego. Nie ma listy argumentów, więc nie może być za długa:

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

I upiększony cuttylko datami, godzinami i nazwami:

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

EDYCJA : Właśnie zauważyłem, że aktualna najwyższa odpowiedź sortuje według daty modyfikacji. Jest to równie łatwe z drugim przykładem tutaj, ponieważ data modyfikacji jest pierwsza w każdym wierszu - klaps sortuj na końcu:

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

Można to również zrobić za pomocą funkcji rekurencyjnej w bash

Niech F wyświetla funkcję pliku, która musi być leksykograficznie sortowalna rrrr-mm-dd itd. (Zależna od systemu operacyjnego?)

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

R funkcja rekurencyjna działająca w katalogach

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

I w końcu

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
Nahuel Fouilleul
źródło