Bagaimana cara menemukan dan membuat daftar file yang dimodifikasi terbaru dalam direktori dengan subdirektori dan waktu?


417
  • Sistem operasi: Linux

  • Jenis sistem file: ext3

  • Solusi yang dipilih: bash (script / oneliner), ruby, python

Saya memiliki beberapa direktori dengan beberapa subdirektori dan file di dalamnya. Saya perlu membuat daftar semua direktori yang dibangun sedemikian rupa sehingga setiap direktori tingkat pertama terdaftar di sebelah tanggal dan waktu dari file yang dibuat / dimodifikasi terbaru di dalamnya.

Untuk memperjelas, jika saya menyentuh file atau mengubah isinya beberapa tingkat subdirektori ke bawah, cap waktu itu harus ditampilkan di sebelah nama direktori tingkat pertama. Katakanlah saya memiliki direktori yang disusun seperti ini:

./alfa/beta/gamma/example.txt

dan saya memodifikasi isi file example.txt, saya perlu waktu itu ditampilkan di sebelah direktori tingkat pertama alfadalam bentuk yang dapat dibaca manusia, bukan zaman. Saya sudah mencoba beberapa hal menggunakan find, xargs, sortdan sejenisnya tapi aku tidak bisa mendapatkan sekitar masalah bahwa timestamp filesystem dari 'alfa' tidak berubah ketika saya membuat / memodifikasi file beberapa tingkat bawah.


Jika Anda dapat menahan rasa sakit karena membangunnya, github.com/shadkam/recentmost dapat digunakan.
user3392225

4
Luar biasa. 16 jawaban, dan kebanyakan / semua bahkan tidak mencoba melakukan apa yang ditentukan OP ...
hmijail berduka pada

Alih-alih solusi seperti saklar -R, saya hanya melihat sebagian besar di sini.
neverMind9

Harus menjadi fitur asli.
neverMind9

Jawaban:


486

Coba yang ini:

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

Jalankan dengan path ke direktori di mana ia harus mulai memindai secara rekursif (mendukung nama file dengan spasi).

Jika ada banyak file mungkin perlu beberapa saat sebelum mengembalikan apa pun. Kinerja dapat ditingkatkan jika kita menggunakan xargssebagai gantinya:

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

yang sedikit lebih cepat.


132
"Metode cepat" Anda juga harus dapat menggunakan print0 untuk mendukung spasi dan bahkan umpan baris dalam nama file. Inilah yang saya gunakan: find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head Ini masih bisa cepat untuk saya.
Dan

20
Pada Mac OS X itu bukan stat GNU jadi perintah gagal. Anda harus brew install coreutilsmenggunakan dan gstatbukannyastat
CharlesB

36
Anda tidak perlu lari statkarena find PATH -type f -printf "%T@ %p\n"| sort -nrmelakukan pekerjaan. Itu juga sedikit lebih cepat seperti itu.
nr

4
bisakah kita mengubah komentar @ user37078 menjadi jawaban aktual atau mengedit jawaban asli? sepertinya "cara yang benar" [tm] untuk melakukannya.
mnagel

6
Pada Mac OS X, tanpa menginstal gstat atau apa pun, Anda dapat melakukan:find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
cobbzilla

198

Untuk menemukan semua file yang status file terakhir diubah N menit yang lalu:

find -cmin -N

sebagai contoh:

find -cmin -5


4
+1 Terima kasih, sangat berguna. Bekerja pada Windows menggunakan GnuWin32 find.
Sabuncu

sangat singkat. sangat bagus!
Randy L

lebih cepat daripada solusi lain yang lebih rumit
david.perez

20
Sangat bagus, Anda juga dapat menggunakan 'find -ctime -50' misalnya untuk 50 hari terakhir perubahan.
Gorkem

1
Untuk mengecualikan kekacauan, gunakansudo find -cmin -1 2>&1 |grep -v /proc/
Cees Timmerman

39

GNU Find (see man find) memiliki -printfparameter untuk mengirim file, EPOC, mtime dan nama path relatif.

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

3
Terima kasih! Ini adalah satu-satunya jawaban yang cukup cepat untuk mencari melalui struktur direktori saya yang sangat luas dalam waktu yang wajar. Saya melewatkan output tailuntuk mencegah ribuan baris dari yang dicetak dalam output.
sffc

8
Komentar lain: awk '{print $2}'bagian ini tampaknya menyebabkan masalah ketika ada nama file dengan spasi. Berikut ini adalah solusi menggunakan sedsebaliknya, dan juga mencetak waktu di samping jalan:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc

3
Saya pikir itu harus menjadi -rn
Bojan Dević

2
Varian -printf jauh lebih cepat daripada memanggil proses 'stat' setiap kali - ini memotong jam dari pekerjaan cadangan saya. Terima kasih telah membuat saya sadar akan hal ini. Saya menghindari awk / sed karena saya hanya khawatir tentang pembaruan terakhir di dalam pohon - jadi X = $ (temukan / path -type f -printf '% T% p \ n' | grep -v sesuatu-I- don-tcare-about | sort -nr | head -n 1) dan gema $ {X # * ""} bekerja dengan baik untuk saya (beri saya barang sampai ruang pertama)
David Goodwin

2
Semua tidak akan berfungsi jika nama file melintasi beberapa baris. Gunakan touch "lala<Enter>b"untuk membuat file seperti itu. Saya pikir desain utilitas unix memiliki kelemahan besar tentang nama file.
Buah

35

Saya mempersingkat jawaban halo yang luar biasa untuk one-liner ini

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

Diperbarui : Jika ada spasi dalam nama file, Anda dapat menggunakan modifikasi ini

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

Bagaimana dengan ini: IFS = $ '\ n'; stat --printf = "% y% n \ n" $ (ls -tr $ (temukan. -type f))
slashdottir

3
Ini tidak akan berfungsi jika Anda memiliki banyak file. jawaban yang menggunakan xargs mengatasi batas itu.
carl verbiest

@carlverbiest memang sejumlah besar file akan merusak solusi slashdottir. Bahkan solusi berbasis xargs akan lambat. solusi user2570243 terbaik untuk sistem file besar.
Stéphane Gourichon

IFS=$'\n'tidak aman dalam hal apa pun saat menangani nama file: Newlines adalah karakter yang valid dalam nama file di UNIX. Hanya karakter NUL yang dijamin tidak akan hadir di jalur.
Charles Duffy

17

Coba ini

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

Ini digunakan finduntuk mengumpulkan semua file dari direktori, lsuntuk daftar mereka diurutkan berdasarkan tanggal modifikasi, headuntuk memilih file 1 dan akhirnya statmenunjukkan waktu dalam format yang bagus.

Saat ini tidak aman untuk file dengan spasi putih atau karakter khusus lainnya dalam namanya. Tulis pujian jika belum memenuhi kebutuhan Anda.


1
halo: Saya suka jawaban Anda, itu berfungsi dengan baik dan mencetak file yang benar. Saya tidak membantu saya karena ada terlalu banyak sublevel dalam kasus saya. Jadi saya mendapatkan "Daftar argumen terlalu lama" untuk ls ... dan xargs tidak akan membantu dalam kasus ini juga. Saya akan mencoba sesuatu yang lain.
fredrik

Dalam hal ini sedikit lebih kompleks dan akan membutuhkan beberapa program nyata. Saya akan meretas beberapa Perl.
Daniel Böhmer

1
Saya memecahkan ini menggunakan PHP sebagai gantinya. Fungsi rekursif yang turun melalui pohon sistem file dan menyimpan waktu file yang paling baru dimodifikasi.
fredrik

11

Perintah ini berfungsi di Mac OS X:

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

Di Linux, seperti yang diminta poster asli, gunakan statsebagai ganti gstat.

Jawaban ini, tentu saja, solusi luar biasa user37078 , dipromosikan dari komentar menjadi jawaban penuh. Saya mencampur wawasan CharlesB untuk digunakan gstatpada Mac OS X. Ngomong -ngomong, saya mendapatkan coreutils dari MacPorts daripada homebrew .

Dan inilah cara saya mengemas ini menjadi perintah sederhana ~/bin/ls-recent.shuntuk digunakan kembali:

#!/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
Pada OS X yosemite; Saya mendapatkan pesan kesalahan: find: ftsopen: Tidak ada file atau direktori seperti itu
Reece

Menarik. Perintah apa yang Anda ketikkan (dengan parameter)? Dan apa nama-nama file dalam direktori itu? Dan jika Anda membuat versi Anda sendiri ~/bin/ls-recent.sh, sudahkah Anda memeriksa skrip untuk perbedaan?
Jim DeLaHunt

10
bagi mereka yang tidak ingin menginstal apa pun di Mac OS X:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake

5

Baik solusi perl dan Python di posting ini membantu saya memecahkan masalah ini di Mac OS X: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively -tidak-stat-perintah-berhasil .

Mengutip dari pos:

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

Mengabaikan file tersembunyi - dengan cap waktu yang bagus & cepat

Menangani spasi dalam nama file dengan baik - bukan berarti Anda harus menggunakannya!

$ 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

Lebih findberlimpah dapat ditemukan dengan mengikuti link.


3

Saya menunjukkan ini untuk waktu akses terbaru, Anda dapat dengan mudah memodifikasi ini untuk melakukan waktu mod terbaru.

Ada dua cara untuk melakukan ini:


1) Jika Anda ingin menghindari penyortiran global yang bisa mahal jika Anda memiliki puluhan juta file, maka Anda dapat melakukannya: (posisikan diri Anda di root direktori tempat Anda ingin memulai pencarian)

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 

Metode di atas mencetak nama file dengan waktu akses yang semakin baru dan file terakhir yang dicetak adalah file dengan waktu akses terbaru. Anda jelas bisa mendapatkan waktu akses terbaru menggunakan "tail -1".


2) Anda dapat menemukan secara rekursif mencetak nama, waktu akses semua file dalam subdirektori Anda dan kemudian mengurutkan berdasarkan waktu akses dan ekor entri terbesar:

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

Dan begitulah ...


3

Saya memiliki alias ini di. Profil yang saya gunakan cukup sering

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

Jadi ia melakukan apa yang Anda cari (dengan pengecualian itu tidak melintasi perubahan tanggal / waktu beberapa level) - mencari file terbaru (* .log dan * .trc file dalam kasus ini); juga hanya menemukan file yang dimodifikasi pada hari terakhir, lalu urutkan berdasarkan waktu dan mengirimkan output melalui lebih sedikit:

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

ps. Perhatikan Saya tidak memiliki root pada beberapa server, tetapi selalu memiliki sudo, jadi Anda mungkin tidak perlu bagian itu.


Bagaimana ini "persis apa yang Anda cari"? OP menulis penjelasan yang bagus tentang apa yang diinginkannya, dan ini sama sekali mengabaikannya.
hmijail meratapi orang-orang yang mengundurkan diri pada

terima kasih telah menunjukkan itu. Anda benar - metode ini tidak berjalan beberapa tingkat untuk mendapatkan perubahan tanggal / waktu, itu hanya menampilkan tanggal / waktu file direktori di dalamnya. edit jawaban saya.
Tagar

1

Anda dapat memberikan perintah printf dari find a try

% Ak File waktu akses terakhir dalam format yang ditentukan oleh k, yang merupakan @' or a directive for the C fungsi strftime '. Nilai yang mungkin untuk k tercantum di bawah ini; beberapa dari mereka mungkin tidak tersedia di semua sistem, karena perbedaan `strftime 'antara sistem.


1

Fungsi bash cepat:

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

Temukan file yang dimodifikasi terbaru dalam direktori:

findLatestModifiedFiles "/home/jason/" 1

Anda juga dapat menentukan format tanggal / waktu Anda sendiri sebagai argumen ketiga.


1

Berikut ini mengembalikan Anda string cap waktu dan nama file dengan cap waktu terbaru:

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

Menghasilkan output berupa: <yy-mm-dd-hh-mm-ss.nanosec> <filename>


1

Berikut adalah satu versi yang berfungsi dengan nama file yang mungkin mengandung spasi, baris baru, karakter glob juga:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printfmencetak modifikasi file (nilai EPOCH) diikuti oleh spasi dan \0nama file yang diakhiri.
  • sort -zk1nr membaca NUL data yang dihentikan dan mengurutkannya secara numerik

Karena pertanyaan ditandai dengan Linux, jadi saya mengasumsikan gnuutils tersedia.

Anda dapat memasang pipa di atas dengan:

xargs -0 printf "%s\n"

untuk mencetak waktu modifikasi dan nama file yang diurutkan berdasarkan waktu modifikasi (paling baru pertama) diakhiri oleh baris baru.


1

Inilah yang saya gunakan (sangat efisien):

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

PROS:

  • hanya memunculkan 3 proses

PEMAKAIAN:

find_last [dir [number]]

dimana:

  • dir - direktori yang akan dicari [dir sekarang]
  • number - jumlah file terbaru untuk ditampilkan [10]

Output untuk find_last /etc 4terlihat seperti ini:

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

Untuk lsoutput biasa , gunakan ini. Tidak ada daftar argumen, jadi tidak bisa terlalu lama:

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

Dan dibenarkan dengan cuthanya untuk tanggal, waktu, dan nama:

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

EDIT : Hanya perhatikan bahwa jawaban teratas saat ini diurutkan berdasarkan tanggal modifikasi. Itu sama mudahnya dengan contoh kedua di sini, karena tanggal modifikasi adalah yang pertama di setiap baris - tampar yang sejenis di bagian akhir:

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

0

Ini bisa dilakukan dengan fungsi rekursif di bash juga

Biarkan F fungsi yang menampilkan waktu file yang harus diurutkan secara leksikografis yyyy-mm-dd dll., (Tergantung os?)

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

R fungsi rekursif yang dijalankan melalui direktori

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

Dan akhirnya

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.