Bagaimana saya bisa menemukan file tertua di pohon direktori

72

Saya mencari shell one-liner untuk menemukan file tertua di pohon direktori.

Marius Gedminas
sumber

Jawaban:

72

Ini berfungsi (diperbarui untuk memasukkan saran Daniel Andersson):

find -type f -printf '%T+ %p\n' | sort | head -n 1
Marius Gedminas
sumber
8
Mengetik lebih sedikit:find -type f -printf '%T+ %p\n' | sort | head -1
Daniel Andersson
1
Saya mendapatkan ruang kosong karena baris pertama saya dari ini findkosong karena saya memiliki nama file yang mengandung baris baru.
林果 皞
1
Bisakah saya bertanya apakah ini menggunakan tanggal dibuat atau modifikasi?
MrMesees
1
Linux tidak menyimpan tanggal pembuatan file di mana pun [*]. Ini menggunakan tanggal modifikasi. [*] ini sebenarnya tidak benar; ext4 menyimpan tanggal pembuatan inode, tetapi tidak diekspos melalui panggilan sistem apa pun dan Anda perlu menggunakan debugfs untuk melihatnya.)
Marius Gedminas
11

Yang ini sedikit lebih portabel dan karena tidak bergantung pada findekstensi GNU -printf, jadi ini berfungsi pada BSD / OS X juga:

find . -type f -print0 | xargs -0 ls -ltr | head -n 1

Satu-satunya downside di sini adalah bahwa itu agak terbatas pada ukuran ARG_MAX(yang seharusnya tidak relevan untuk kebanyakan kernel yang lebih baru). Jadi, jika ada lebih dari getconf ARG_MAXkarakter yang dikembalikan (262.144 di sistem saya), itu tidak memberi Anda hasil yang benar. Ini juga tidak sesuai dengan POSIX karena -print0dan xargs -0tidak.

Beberapa solusi lebih lanjut untuk masalah ini diuraikan di sini: Bagaimana saya bisa menemukan file terbaru (terbaru, paling awal, tertua) dalam direktori? - Greg's Wiki

slhck
sumber
Ini juga berfungsi, tetapi juga memancarkan xargs: ls: terminated by signal 13kesalahan sebagai efek samping. Saya menduga itu SIGPIPE. Saya tidak tahu mengapa saya tidak mendapatkan kesalahan yang sama ketika saya menyortir output sort ke head dalam solusi saya.
Marius Gedminas
Versi Anda juga lebih mudah untuk diketik dari memori. :-)
Marius Gedminas
Ya, itu pipa yang rusak. Saya tidak mendapatkan ini dengan versi GNU dan BSD dari semua perintah itu, tapi itu adalah headperintah yang berhenti setelah membaca baris dan dengan demikian "mematahkan" pipa, saya pikir. Anda tidak mendapatkan kesalahan karena sorttampaknya tidak mengeluh tentang hal itu, tetapi lsmelakukannya dalam kasus lain.
slhck
4
Ini rusak jika ada begitu banyak nama file yang xargsperlu dipanggil lslebih dari sekali. Dalam hal ini, output yang diurutkan dari beberapa pemanggilan itu berakhir disatukan ketika mereka harus digabung.
Nicole Hamilton
2
Saya pikir ini lebih buruk daripada memposting skrip yang menganggap nama file tidak pernah mengandung spasi. Banyak waktu, itu akan berhasil karena nama file tidak memiliki spasi. Dan ketika mereka gagal, Anda mendapatkan kesalahan. Tetapi ini tidak mungkin berhasil dalam kasus nyata dan kegagalan tidak akan ditemukan. Pada pohon direktori apa pun yang cukup besar sehingga Anda tidak bisa lsmelakukannya dan melihat file tertua, solusi Anda mungkin akan melampaui batas panjang baris perintah, yang menyebabkan lsdipanggil beberapa kali. Anda akan mendapatkan jawaban yang salah tetapi Anda tidak akan pernah tahu.
Nicole Hamilton
11

Perintah perintah berikut dijamin untuk bekerja dengan segala jenis nama file aneh:

find -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat

find -type f -printf "%T@ %T+ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'

stat -c "%y %n" "$(find -type f -printf "%T@ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"

Menggunakan byte nol ( \0) alih-alih karakter linefeed ( \n) memastikan output dari find masih dapat dimengerti jika salah satu nama file berisi karakter linefeed.

The -zswitch membuat kedua mengurutkan dan grep menafsirkan hanya byte null sebagai end-of-line karakter. Karena tidak ada saklar untuk head, kami menggunakan grep -m 1(hanya satu kejadian).

Perintah diperintahkan oleh waktu eksekusi (diukur pada mesin saya).

  • Perintah pertama akan menjadi yang paling lambat karena harus mengubah waktu setiap file menjadi format yang dapat dibaca manusia terlebih dahulu dan kemudian mengurutkan string tersebut. Perpipaan ke kucing menghindari pewarnaan hasil.

  • Perintah kedua sedikit lebih cepat. Sementara itu masih melakukan konversi tanggal, penyortiran numerik ( sort -n) detik berlalu sejak zaman Unix sedikit lebih cepat. sed menghapus detik sejak zaman Unix.

  • Perintah terakhir tidak melakukan konversi sama sekali dan harus secara signifikan lebih cepat daripada dua yang pertama. Perintah find sendiri tidak akan menampilkan mtime dari file terlama, jadi stat diperlukan.

Halaman manual terkait: find - grep - sed - sort - stat

Dennis
sumber
5

Meskipun jawaban yang diterima dan yang lainnya di sini melakukan pekerjaan, jika Anda memiliki pohon yang sangat besar, semuanya akan mengurutkan seluruh file.

Lebih baik jika kita bisa daftar mereka dan melacak yang tertua, tanpa perlu menyortir sama sekali.

Itulah mengapa saya datang dengan solusi alternatif ini:

ls -lRU $PWD/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($1,0,1)=="/") { pat=substr($1,0,length($0)-1)"/"; }; if( $6 != "") {if ( $6 < oldd ) { oldd=$6; oldf=pat$8; }; print $6, pat$8; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

Saya harap ini bisa membantu, bahkan jika pertanyaannya agak lama.


Sunting 1: perubahan ini memungkinkan penguraian file dan direktori dengan spasi. Cukup cepat untuk mengeluarkannya di root /dan menemukan file terlama yang pernah ada.

ls -lRU --time-style=long-iso "$PWD"/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($0,0,1)=="/") { pat=substr($0,0,length($0)-1)"/"; $6="" }; if( $6 ~ /^[0-9]+$/) {if ( $6 < oldd ) { oldd=$6; oldf=$8; for(i=9; i<=NF; i++) oldf=oldf $i; oldf=pat oldf; }; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

Perintah dijelaskan:

  • ls -lRU - time-style = long-iso "$ PWD" / * mencantumkan semua file (*), format panjang (l), secara rekursif (R), tanpa mengurutkan (U) menjadi cepat, dan pipa untuk awk
  • Awk kemudian DIMULAI dengan memberi nol penghitung (opsional untuk pertanyaan ini) dan mengatur tanggal terlama menjadi hari ini, format YearMonthDay.
  • Loop utama pertama
    • Raih bidang ke-6, tanggal, format Tahun-Bulan-Hari, dan ubah ke YearMonthDay (jika ls Anda tidak menampilkan dengan cara ini, Anda mungkin perlu mengaturnya).
    • Menggunakan rekursif, akan ada baris header untuk semua direktori, dalam bentuk / direktori / di sini :. Grab baris ini menjadi variabel tepuk. (mengganti ":" menjadi "/" yang terakhir). Dan set $ 6 menjadi tidak ada untuk menghindari penggunaan baris header sebagai baris file yang valid.
    • jika bidang $ 6 memiliki angka yang valid, ini tanggal. Bandingkan dengan tanggal lama.
    • Apakah ini lebih tua? Kemudian simpan nilai baru untuk old oldd dan nama file oldf. BTW, oldf tidak hanya bidang 8, tetapi dari 8 sampai akhir. Itu sebabnya loop untuk menyatukan dari 8 ke NF (akhir).
    • Hitung uang muka satu per satu
    • AKHIR dengan mencetak hasilnya

Menjalankannya:

~ $ time ls -lRU "$ PWD" / * | awk dll.

Tanggal terlama: 19691231

File: /home/.../.../backupold/.../EXAMPLES/how-to-program.txt

Total dibandingkan: 111438

0m1.135s nyata

pengguna 0m0.872s

sys 0m0.760s


EDIT 2: Konsep yang sama, solusi yang lebih baik digunakan finduntuk melihat waktu akses (digunakan %Tdengan yang pertama printfuntuk waktu modifikasi atau %Cuntuk perubahan status sebagai gantinya).

find . -wholename "*" -type f -printf "%AY%Am%Ad %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

EDIT 3: Perintah di bawah ini menggunakan waktu modifikasi dan juga mencetak progres tambahan saat menemukan file yang lebih lama dan lebih lama, yang berguna ketika Anda memiliki cap waktu yang salah (seperti 1970-01-01):

find . -wholename "*" -type f -printf "%TY%Tm%Td %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; print oldd " " oldf; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
Dr Beco
sumber
Masih perlu tweeking untuk menerima file dengan spasi. Saya akan segera melakukannya.
Dr Beco
Saya pikir parsing ls untuk file dengan spasi bukanlah ide yang baik. Mungkin menggunakan find.
Dr Beco
Jalankan saja di seluruh pohon "/". Waktu yang dihabiskan: Total dibandingkan: 585744 pengguna real 2m14.017s 0m8.181s sys 0m8.473s
Dr Beco
Penggunaan lsburuk untuk scripting karena outputnya tidak dimaksudkan untuk mesin, format output bervariasi di seluruh implementasi. Seperti yang sudah Anda nyatakan findbagus untuk scripting tetapi mungkin juga bagus untuk menambahkan info itu sebelum menceritakan lssolusi.
Sampo Sarrala
4

Silakan gunakan ls - halaman manual memberi tahu Anda cara memesan direktori.

ls -clt | head -n 2

-N 2 adalah sehingga Anda tidak mendapatkan "total" di output. Jika Anda hanya menginginkan nama file.

ls -t | head -n 1

Dan jika Anda membutuhkan daftar dalam urutan normal (mendapatkan file terbaru)

ls -tr | head -n 1

Jauh lebih mudah daripada menggunakan find, jauh lebih cepat, dan lebih kuat - tidak perlu khawatir tentang format penamaan file. Ini seharusnya bekerja pada hampir semua sistem juga.

pengguna1363990
sumber
6
Ini hanya berfungsi jika file berada di direktori tunggal, sementara pertanyaan saya adalah tentang pohon direktori.
Marius Gedminas
2
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
Okki
sumber
Ini tidak akan berfungsi dengan baik jika ada file yang lebih tua dari 9 Sep 2001 (1000000000 detik sejak zaman Unix). Untuk mengaktifkan penyortiran angka, gunakan sort -n.
Dennis
Ini membantu menemukan saya file, tetapi sulit untuk melihat berapa umurnya tanpa menjalankan perintah kedua :)
Marius Gedminas
0

Tampaknya oleh "tertua" kebanyakan orang berasumsi bahwa Anda berarti "waktu modifikasi tertua." Itu mungkin diperbaiki, menurut interpretasi paling ketat dari "tertua", tetapi jika Anda menginginkan yang memiliki waktu akses terlama , saya akan mengubah jawaban terbaik sebagai berikut:

find -type f -printf '%A+ %p\n' | sort | head -n 1

Perhatikan %A+.

PenguinLust
sumber
-1
set $(find /search/dirname -type f -printf '%T+ %h/%f\n' | sort | head -n 1) && echo $2
  • find ./search/dirname -type f -printf '%T+ %h/%f\n' mencetak tanggal dan nama file dalam dua kolom.
  • sort | head -n1 menjaga garis yang sesuai dengan file terlama.
  • echo $2 menampilkan kolom kedua, yaitu nama file.
Dima
sumber
1
Selamat Datang di Pengguna Super! Meskipun ini mungkin menjawab pertanyaan, itu akan menjadi jawaban yang lebih baik jika Anda bisa memberikan penjelasan mengapa itu bisa terjadi.
DavidPostill
1
Perhatikan, beberapa orang juga meminta penjelasan tentang jawaban Anda yang dihapus (identik) sebelumnya.
DavidPostill
Apa yang sulit dijawab? find ./search/dirname -type f -printf '% T +% h /% f \ n' | sortir | head -n 1 Ini menunjukkan dua kolom sebagai waktu dan jalur file. Perlu untuk menghapus kolom pertama. Menggunakan set dan echo $ 2
Dima
1
Anda harus memberikan penjelasan alih-alih hanya menempelkan baris perintah, seperti yang diminta oleh beberapa pengguna lain.
Ob1lan
1
Bagaimana ini berbeda dari jawaban yang diterima?
Ramhound