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ół.


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.


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


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

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";

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.


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

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'

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.


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 ...


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.


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.


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.


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>


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.


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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.