Alt dizinleri ve süreleri olan bir dizindeki en son değiştirilen dosyaları özyinelemeli olarak nasıl bulur ve listeler?


417
  • İşletim sistemi: Linux

  • Dosya sistemi türü: ext3

  • Tercih edilen çözüm: bash (script / oneliner), ruby, python

İçinde birkaç alt dizin ve dosyaları ile birkaç dizin var. Her birinci düzey dizini, içinde en son oluşturulan / değiştirilmiş dosyanın tarih ve saatinin yanında listelenecek şekilde yapılandırılmış tüm bu dizinlerin bir listesini yapmak gerekir.

Açıklığa kavuşturmak için, bir dosyaya dokunduğumda veya içeriğini birkaç alt dizin düzeyinde aşağı değiştirdiğimde, bu zaman damgası birinci düzey dizin adının yanında görüntülenmelidir. Diyelim ki bu şekilde yapılandırılmış bir dizin var:

./alfa/beta/gamma/example.txt

ve ben dosyanın içeriğini değiştirmek, example.txto zaman alfadeğil, insan okunabilir formda birinci düzey dizinin yanında görüntülenen zamana ihtiyacım var . Ben, bul kullanarak bazı şeyleri denedim xargs, sortve seviyor ama ben 'alfa' bir dosya sistemi damgası ı oluşturduğunuzda / değiştirmek dosyaları birkaç seviyeleri aşağı değiştirmez sorunu etrafında alınamıyor.


Eğer binanın acısını çekebilirseniz, github.com/shadkam/recentmost kullanılabilir.
user3392225

4
İnanılmaz. 16 cevap ve çoğu / hepsi OP'nin belirttiği şeyi yapmaya bile çalışmıyor ...
hmijail resignees

-R anahtarı gibi çözümler yerine, burada toplu olarak görüyorum.
neverMind9

Yerel bir özellik olmalı.
neverMind9

Yanıtlar:


486

Bunu dene:

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

Özyinelemeli olarak taramaya başlaması gereken dizine giden yolla yürütün (boşluklu dosya adlarını destekler).

Çok fazla dosya varsa, herhangi bir şey döndürmeden önce biraz zaman alabilir. xargsBunun yerine kullanırsak performans iyileştirilebilir :

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

ki bu biraz daha hızlı.


132
"Hızlı yönteminiz" dosya adlarındaki boşlukları ve hatta satır beslemelerini desteklemek için print0'ı da kullanabilmelidir. İşte kullandığım: find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head Bu hala benim için hızlı olmayı başarıyor.
Dan

20
Mac OS X'te GNU'nun stat değeri değildir, bu nedenle komut başarısız olur. Bunun yerine brew install coreutilskullanmanız ve kullanmanız gerekgstatstat
CharlesB

36
İşi yaptığından statberi find PATH -type f -printf "%T@ %p\n"| sort -nrkoşmana gerek yok. Ayrıca bu şekilde biraz daha hızlı.
nr

4
@ user37078'in yorumunu bir şekilde gerçek bir cevaba çevirebilir miyiz veya orijinal cevabı düzenleyebilir miyiz? bunu yapmak için "doğru yol" [tm] gibi görünüyor.
mnagel

6
Mac OS X'te, gstat veya başka bir şey kurmadan şunları yapabilirsiniz:find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
cobbzilla

198

Dosya durumunun en son değiştirildiği tüm dosyaları N dakika önce bulmak için:

find -cmin -N

Örneğin:

find -cmin -5


4
+1 Teşekkürler, çok faydalı. GnuWin32 find kullanarak Windows üzerinde çalışır.
Sabuncu

çok kısa. çok hoş!
Randy L

daha karmaşık diğer çözümlere göre daha hızlı
david.perez

20
Gerçekten iyi, ayrıca son 50 günlük değişiklik için 'find -ctime -50' kullanabilirsiniz.
Görkem

1
sudo find -cmin -1 2>&1 |grep -v /proc/
Karmaşayı

39

GNU Find (bkz. man find) -printfDosyaları EPOC mtime ve göreceli yol adını dağıtmak için bir parametreye sahiptir .

redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'

3
Teşekkürler! Çok geniş dizin yapımı makul bir sürede aramak için yeterince hızlı olan tek cevap bu. tailÇıktıya binlerce satır basılmasını önlemek için çıktıyı geçiyorum .
sffc

8
Başka bir yorum: awk '{print $2}'boşluklu dosya adları olduğunda bölüm sorunlara neden görünüyor. İşte sedbunun yerine kullanarak bir çözüm ve aynı zamanda yola ek olarak zaman yazdırır:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc

3
Bence sıralı olmalı
Bojan Dević

2
-Printf varyantı her seferinde bir 'stat' işlemi çağırmaktan çok daha hızlıdır - yedekleme işlerimden saatlerimi keser. Beni bunun hakkında bilgilendirdiğin için teşekkürler. Ben awk / sed şeyden sadece ağaçtaki son güncellemeden endişe duyduğumdan kaçındım - bu yüzden X = $ (find / path -type f -printf '% T% p \ n' | grep -v bir şey-I- don-tcare-about | sort -nr | head -n 1) ve yankı $ {X # * ""} benim için iyi çalıştı (bana ilk alana kadar şeyler ver)
David Goodwin

2
Birden çok satırda dosya adı varsa tümü çalışmaz. touch "lala<Enter>b"Böyle bir dosya oluşturmak için kullanın . Unix yardımcı programları tasarımının dosya adı konusunda büyük kusuru olduğunu düşünüyorum.
Meyve

35

Halo'nun bu tek astara verdiği müthiş cevabı kısalttım

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

Güncellendi : Dosya adlarında boşluk varsa, bu değişikliği kullanabilirsiniz

OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";

Buna ne dersiniz: IFS = $ '\ n'; stat --printf = "% y% n \ n" $ (ls -tr $ (bul. -type f))
slashdottir

3
Çok sayıda dosyanız varsa bu çalışmaz. xargs kullanan cevaplar bu sınırı çözer.
carl verbiest

@carlverbiest gerçekten çok sayıda dosya slashdottir'in çözümünü bozacaktır. Xargs tabanlı çözümler bile o zaman yavaş olacaktır. user2570243'ün çözümü büyük dosya sistemleri için en iyisidir.
Stéphane Gourichon

IFS=$'\n'dosya adlarını işlerken hiçbir durumda güvenli değildir: Yeni satırlar UNIX'teki dosya adlarında geçerli karakterlerdir. Yalnızca NUL karakterinin bir yolda bulunmaması garanti edilir.
Charles Duffy

17

Bunu dene

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

O kullanır find, dizinin tüm dosyalar toplamak için lsonları, değişiklik tarihine göre sıralanmış listelemek için head1 dosyasını seçmek amacıyla ve son olarak statgüzel bir biçimde zaman gösterecek.

Şu anda, adlarında boşluk veya diğer özel karakterlere sahip dosyalar için güvenli değildir. Henüz ihtiyaçlarınızı karşılamıyorsa bir övgü yazın.


1
hale: Cevabınızı beğendim, iyi çalışıyor ve doğru dosyayı yazdırıyor. Ancak bana yardım etmiyorum çünkü benim durumumda çok fazla alt seviye var. Bu yüzden ls için "Argüman listesi çok uzun" alıyorum ... ve xargs da bu durumda yardımcı olmaz. Başka bir şey deneyeceğim.
fredrik

Bu durumda biraz daha karmaşıktır ve gerçek bir programa ihtiyaç duyacaktır. Biraz Perl'i hackleyeceğim.
Daniel Böhmer

1
Bunun yerine PHP kullanarak çözdüm. Dosya sistemi ağacında inen ve en son değiştirilen dosyanın zamanını saklayan özyinelemeli işlev.
fredrik

11

Bu komut Mac OS X'te çalışır:

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

Linux'ta, orijinal posterin istediği gibi, statyerine kullanın gstat.

Bu cevap, elbette, user37078'in olağanüstü çözümü, yorumdan tam cevaba yükseltildi. Ben karışık CharlesB kullanımına 'ın fikir gstatvar Mac OS X'in I coreutils gelen MacPorts ziyade homebrew arada.

Ve işte bunu ~/bin/ls-recent.shyeniden kullanım için basit bir komut halinde paketledim :

#!/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
OS X yosemite üzerinde; Hata alıyorum: find: ftsopen: Böyle bir dosya veya dizin yok
Reece

İlginç. Hangi komutu yazdınız (parametrelerle)? Ve o dizindeki dosyaların isimleri neydi? Kendi sürümünüzü oluşturduysanız ~/bin/ls-recent.sh, komut dosyasını farklılıklar açısından dikkatlice kontrol ettiniz mi?
Jim DeLaHunt

10
Mac OS X'e bir şey yüklemek istemeyenler için:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake

5

Bu yazıdaki perl ve Python çözümleri, Mac OS X'te bu sorunu çözmeme yardımcı oldu: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively -no-stat-command-avail .

Gönderiden alıntı:

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'

5

Gizli dosyaları yok saymak - güzel ve hızlı zaman damgasıyla

Dosya adlarındaki boşlukları iyi işler - bunları kullanmamalısınız!

$ 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

findBağlantıyı takip ederek daha fazla bolluk bulabilirsiniz.


3

Bunu en son erişim zamanı için gösteriyorum, en son mod süresini yapmak için bunu kolayca değiştirebilirsiniz.

Bunu yapmanın iki yolu vardır:


1) On milyonlarca dosyanız varsa pahalı olabilen küresel sıralamayı önlemek istiyorsanız, şunları yapabilirsiniz: (kendinizi, aramanızın başlamasını istediğiniz dizinin köküne konumlandırın)

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 

Yukarıdaki yöntem, dosya adlarını giderek daha yeni erişim süresiyle yazdırır ve yazdırdığı son dosya en son erişim süresine sahip dosyadır. Açıkçası bir "kuyruk -1" kullanarak en son erişim zamanını alabilirsiniz.


2) Alt dizininizdeki tüm dosyaların adını, erişim süresini tekrar tekrar yazdırabilir ve daha sonra erişim süresine ve kuyruğa en büyük girdiye göre sıralayabilirsiniz:

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

İşte buyur...


3

.Profile dosyamda oldukça sık kullandığım bu takma ad var

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

Böylece aradığınızı yapar (istisna olarak, tarih / saati birden fazla seviyeyi değiştirmez) - en son dosyaları arar (bu durumda * .log ve * .trc dosyaları); ayrıca yalnızca son gün değiştirilen dosyaları bulur, daha sonra zamana göre sıralar ve çıktıyı daha az yoluyla yönlendirir:

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

ps. Dikkat Bazı sunucularda kök yok, ama her zaman sudo var, bu yüzden bu bölüme ihtiyacınız olmayabilir.


Bu "tam olarak aradığın şey" nasıl? OP istediği hakkında iyi bir açıklama yazdı ve bu onu tamamen görmezden geliyor.
hmijail resignees

işaret ettiğin için teşekkürler. haklısınız - bu yöntem tarih / saat değişikliği almak için birden fazla seviyeye gitmiyor, sadece içindeki dizin dosyalarının tarih / saatini gösteriyor. cevabımı düzenledi.
Tagar

1

Denemek için printf komutunu verebilirsiniz.

% Ak Dosyasının @' or a directive for the C strftime işlevi olan k ile belirtilen formattaki son erişim süresi . K için olası değerler aşağıda listelenmiştir; bazı sistemler arasındaki `` strftime '' farklılıkları nedeniyle tüm sistemlerde bulunmayabilir.


1

Hızlı bash işlevi:

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

En son değiştirilen dosyayı bir dizinde bulun:

findLatestModifiedFiles "/home/jason/" 1

Üçüncü bağımsız değişken olarak kendi tarih / saat biçiminizi de belirleyebilirsiniz.


1

Aşağıda, zaman damgasının bir dizesini ve en son zaman damgasına sahip dosyanın adını döndürür:

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

Formun çıktısıyla sonuçlanan: <yy-mm-dd-hh-mm-ss.nanosec> <filename>


1

Alanlar, yeni satırlar, glob karakterler de içerebilen dosya adlarıyla çalışan bir sürüm:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printfdosya değişikliği (EPOCH değeri) ve ardından bir boşluk ve \0sonlandırılmış dosya adları yazdırır .
  • sort -zk1nr NUL sonlandırılmış verileri okur ve sayısal olarak tersine sıralar

Soru Linux ile etiketlendiğinden gnuaraçların mevcut olduğunu varsayıyorum .

Yukarıdakileri boru ile yapabilirsiniz:

xargs -0 printf "%s\n"

yeni satırlarla sonlandırılan değişiklik süresine (en son ilk) göre sıralanmış değişiklik zamanını ve dosya adlarını yazdırmak için.


1

Ben kullanıyorum (çok verimli):

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

Artıları:

  • sadece 3 işlemi doğurur

KULLANIM:

find_last [dir [number]]

nerede:

  • dir - aranacak bir dizin [geçerli dizin]
  • number - görüntülenecek en yeni dosya sayısı [10]

Şuna find_last /etc 4benzer çıktı :

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

Düz lsçıktı için bunu kullanın. Argüman listesi yok, bu yüzden çok uzun olamaz:

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

Ve cutsadece tarihler, saatler ve isim için güzelleştirildi :

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

DÜZENLEME : Sadece mevcut üst yanıtın değişiklik tarihine göre sıralandığını fark ettim. Buradaki ikinci örnekle bu kadar kolay, çünkü değişiklik tarihi her satırda ilk olduğu için - sonuna bir çeşit tokat at:

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

0

Bu bash'da yinelemeli bir işlevle de yapılabilir

F ifadesinin sözlükbilimsel olarak sıralanabilir yyyy-aa-gg vb. Biçiminde görüntülenmesi gereken bir işlevi görüntüleyelim. (İşletim sistemine bağlı mı?)

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

D dizinleri üzerinden çalışan özyinelemeli işlev

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

Ve sonunda

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.