จะค้นหาและแสดงรายการไฟล์ที่แก้ไขล่าสุดซ้ำในไดเรกทอรีที่มีไดเรกทอรีย่อยและเวลาได้อย่างไร


417
  • ระบบปฏิบัติการ: Linux

  • ประเภทระบบไฟล์: ext3

  • วิธีแก้ปัญหาที่ต้องการ: bash (script / oneliner), ruby, python

ฉันมีหลายไดเรกทอรีที่มีหลายไดเรกทอรีย่อยและไฟล์ในนั้น ฉันต้องการสร้างรายชื่อของไดเรกทอรีเหล่านี้ทั้งหมดที่สร้างขึ้นในลักษณะที่ว่าทุกไดเรกทอรีระดับแรกจะแสดงรายการถัดจากวันที่และเวลาของไฟล์ที่สร้าง / แก้ไขล่าสุดภายในนั้น

หากต้องการชี้แจงหากฉันแตะไฟล์หรือแก้ไขเนื้อหาในไดเรกทอรีย่อยไม่กี่ระดับลงการประทับเวลานั้นควรจะปรากฏถัดจากชื่อไดเรกทอรีระดับแรก ว่าฉันมีไดเรกทอรีโครงสร้างเช่นนี้

./alfa/beta/gamma/example.txt

และฉันแก้ไขเนื้อหาของไฟล์example.txtฉันต้องการเวลาที่แสดงถัดจากไดเรกทอรีระดับแรกalfaในรูปแบบที่มนุษย์สามารถอ่านได้ไม่ใช่ยุค ฉันได้พยายามบางสิ่งบางอย่างที่ใช้ค้นหาxargs, sortและชอบ แต่ฉันไม่สามารถได้รับการแก้ไขปัญหาที่ประทับเวลาระบบแฟ้มของ 'อัลฟ่า' ไม่เปลี่ยนแปลงเมื่อฉันสร้าง / แก้ไขไฟล์ที่ระดับไม่กี่ลง


หากคุณสามารถสร้างความเจ็บปวดได้คุณสามารถใช้github.com/shadkam/recentmostได้
user3392225

4
เหลือเชื่อ 16 คำตอบและมากที่สุด / ทั้งหมดไม่ได้พยายามที่จะทำในสิ่งที่ OP ที่ระบุ ...
ก็ไว้ทุกข์ hmijail resignees

แทนที่จะเป็นโซลูชันเช่นสวิตช์ -R ฉันเห็นเป็นกลุ่มที่นี่
neverMind9

ควรเป็นคุณสมบัติดั้งเดิม
neverMind9

คำตอบ:


486

ลองอันนี้:

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

ดำเนินการด้วยพา ธ ไปยังไดเรกทอรีที่ควรเริ่มการสแกนซ้ำ (รองรับชื่อไฟล์ที่มีช่องว่าง)

หากมีไฟล์จำนวนมากอาจใช้เวลาสักครู่ก่อนที่จะส่งคืนสิ่งใด สามารถปรับปรุงประสิทธิภาพได้หากเราใช้xargsแทน:

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

ซึ่งเร็วกว่านิดหน่อย


132
"วิธีการเร็ว" ของคุณควรจะสามารถใช้ print0 เพื่อรองรับการเว้นวรรคและแม้กระทั่ง linefeeds ในชื่อไฟล์ นี่คือสิ่งที่ฉันใช้: find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head นี่ยังคงจัดการได้อย่างรวดเร็วสำหรับฉัน
Dan

20
ใน Mac OS X ไม่ใช่คำสั่งสถิติของ GNU ดังนั้นจึงล้มเหลว คุณต้องbrew install coreutilsใช้และใช้gstatแทนstat
CharlesB

36
คุณไม่จำเป็นต้องทำงานstatตั้งแต่find PATH -type f -printf "%T@ %p\n"| sort -nrทำงาน มันก็เร็วขึ้นเล็กน้อยเช่นกัน
ไม่มี

4
เราสามารถเปลี่ยนความคิดเห็นของ @ user37078 ให้เป็นคำตอบจริงหรือแก้ไขคำตอบเดิมได้หรือไม่? ดูเหมือนว่าจะเป็น "วิธีที่ถูกต้อง" [tm] ที่จะทำ
mnagel

6
ใน Mac OS X โดยไม่ต้องติดตั้ง gstat หรือสิ่งอื่นใดคุณสามารถทำได้:find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
cobbzilla

198

หากต้องการค้นหาไฟล์ทั้งหมดที่สถานะไฟล์ถูกเปลี่ยนล่าสุดNนาทีที่ผ่านมา:

find -cmin -N

ตัวอย่างเช่น:

find -cmin -5


4
+1 ขอบคุณมีประโยชน์มาก ทำงานบน Windows โดยใช้ GnuWin32 find
Sabuncu

สั้นมาก ดีมาก!
แรนดี้ L

มันเร็วกว่าโซลูชันอื่น ๆ ที่ซับซ้อนกว่านี้
david.perez

20
ดีมากนอกจากนี้คุณยังสามารถใช้ 'find -ctime -50' สำหรับการเปลี่ยนแปลงในช่วง 50 วันที่ผ่านมา
Gorkem

1
หากต้องการยกเว้นความยุ่งเหยิงให้ใช้sudo find -cmin -1 2>&1 |grep -v /proc/
Cees Timmerman

39

GNU Find (ดูman find) มี-printfพารามิเตอร์สำหรับแยกไฟล์ EPOC mtime และชื่อพา ธ สัมพัทธ์

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

3
ขอบคุณ! นี่เป็นคำตอบเดียวที่เร็วพอที่จะค้นหาในโครงสร้างไดเรกทอรีที่กว้างมากของฉันในเวลาที่เหมาะสม ฉันส่งเอาต์พุตtailไปเพื่อป้องกันไม่ให้พันบรรทัดถูกพิมพ์ในเอาต์พุต
sffc

8
ความคิดเห็นอื่น: awk '{print $2}'ส่วนที่ดูเหมือนจะทำให้เกิดปัญหาเมื่อมีชื่อไฟล์ที่มีช่องว่าง นี่คือวิธีการแก้ปัญหาโดยใช้sedแทนและมันยังพิมพ์เวลาที่นอกเหนือไปจากเส้นทาง:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc

3
ฉันคิดว่ามันควรจะเรียงลำดับ -rn
Bojan Dević

2
ตัวแปร -printf นั้นเร็วกว่าการเรียกกระบวนการ 'สถิติ' ในแต่ละครั้ง - มันตัดชั่วโมงสำรองงานของฉัน ขอบคุณที่ทำให้ฉันตระหนักถึงสิ่งนี้ ฉันหลีกเลี่ยงสิ่ง awk / sed ตามที่ฉันกังวลเฉพาะเกี่ยวกับการปรับปรุงล่าสุดภายในทรี - ดังนั้น X = $ (ค้นหา / เส้นทาง -type f -printf '% T% p% n \ \' | grep -v บางอย่าง -I- don-tcare-about | sort -nr | head -n 1) และเสียงก้อง $ {X # * ""} ทำงานได้ดีสำหรับฉัน (ให้ข้อมูลกับฉันถึงที่ว่างแรก)
David Goodwin

2
ทั้งหมดจะไม่ทำงานหากชื่อไฟล์ข้ามหลายบรรทัด ใช้touch "lala<Enter>b"สำหรับสร้างไฟล์ดังกล่าว ฉันคิดว่าการออกแบบระบบสาธารณูปโภคของยูนิกซ์มีข้อบกพร่องมากมายเกี่ยวกับชื่อไฟล์
ผลไม้

35

ฉันทำให้คำตอบที่น่าประทับใจของฮาโลสั้นลง

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

อัปเดต : หากมีช่องว่างในชื่อไฟล์คุณสามารถใช้การแก้ไขนี้ได้

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

แล้วเรื่องนี้: IFS = $ '\ n'; stat --printf = "% y% n \ n" $ (ls -tr $ (ค้นหา. -type f))
slashdottir

3
สิ่งนี้จะไม่ทำงานหากคุณมีไฟล์จำนวนมาก คำตอบที่ใช้ xargs แก้ข้อ จำกัด นั้น
คาร์ล verbiest

@carlverbiest แน่นอนไฟล์จำนวนมากจะทำลายโซลูชันของ slashdottir แม้แต่โซลูชั่นที่ใช้ xargs ก็จะช้าเช่นกัน โซลูชันของ user2570243นั้นดีที่สุดสำหรับระบบไฟล์ขนาดใหญ่
Stéphane Gourichon

IFS=$'\n'ไม่ปลอดภัยในทุกกรณีเมื่อจัดการชื่อไฟล์: บรรทัดใหม่เป็นอักขระที่ถูกต้องในชื่อไฟล์ใน UNIX เฉพาะอักขระ NUL เท่านั้นที่รับประกันว่าจะไม่ปรากฏในเส้นทาง
Charles Duffy

17

ลองสิ่งนี้

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

มันใช้findเพื่อรวบรวมไฟล์ทั้งหมดจากไดเรกทอรีlsเพื่อแสดงรายการพวกเขาเรียงตามวันที่แก้ไขheadสำหรับการเลือกไฟล์ที่ 1 และในที่สุดstatจะแสดงเวลาในรูปแบบที่ดี

ในเวลานี้มันไม่ปลอดภัยสำหรับไฟล์ที่มีช่องว่างหรือตัวอักษรพิเศษอื่น ๆ ในชื่อของพวกเขา เขียนคำยกย่องหากยังไม่ตรงกับความต้องการของคุณ


1
halo: ฉันชอบคำตอบของคุณมันทำงานได้ดีและพิมพ์ไฟล์ที่ถูกต้อง ฉันไม่ได้ช่วย แต่เนื่องจากมี sublevels มากเกินไปในกรณีของฉัน ดังนั้นฉันจึงได้รับ "รายการอาร์กิวเมนต์ยาวเกินไป" สำหรับ ls ... และ xargs จะไม่ช่วยในกรณีนี้เช่นกัน ฉันจะลองอย่างอื่น
fredrik

ในกรณีนี้มันค่อนข้างซับซ้อนและต้องใช้โปรแกรมจริง ฉันจะแฮ็ก Perl บางอัน
Daniel Böhmer

1
ฉันแก้ไขมันโดยใช้ PHP แทน ฟังก์ชั่นวนซ้ำที่สืบทอดมาผ่านทรีของระบบไฟล์และเก็บเวลาของไฟล์ที่แก้ไขล่าสุด
fredrik

11

คำสั่งนี้ใช้ได้กับ Mac OS X:

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

บน Linux เป็นโปสเตอร์เดิมถามว่าใช้แทนstatgstat

แน่นอนคำตอบนี้แน่นอนว่าโซลูชันที่ยอดเยี่ยมของuser37078ได้เลื่อนระดับจากความคิดเห็นไปเป็นคำตอบแบบเต็ม ฉันใช้ข้อมูลเชิงลึกของCharlesBเพื่อใช้gstatกับ Mac OS X ฉันได้รับcoreutilsจากMacPortมากกว่าhomebrewตามวิธี

และนี่คือวิธีที่ฉันบรรจุสิ่งนี้ลงในคำสั่งง่ายๆ~/bin/ls-recent.shสำหรับการใช้ซ้ำ:

#!/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; ฉันได้รับข้อผิดพลาด: ค้นหา: ftsopen: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
Reece

น่าสนใจ คุณพิมพ์คำสั่งอะไร (พร้อมพารามิเตอร์) และชื่อของไฟล์ในไดเรกทอรีนั้นคืออะไร? และถ้าคุณสร้างเวอร์ชันของคุณเอง~/bin/ls-recent.shคุณตรวจสอบความแตกต่างของสคริปต์อย่างระมัดระวังหรือไม่?
Jim DeLaHunt

10
สำหรับผู้ที่ไม่ต้องการติดตั้งอะไรบน Mac OS X:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake

5

ทั้งโซลูชัน perl และ Python ในโพสต์นี้ช่วยฉันแก้ปัญหานี้ใน Mac OS X: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively ไม่มี-สถิติ-คำสั่งประโยชน์

การอ้างอิงจากโพสต์:

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

งูหลาม:

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

ไม่สนใจไฟล์ที่ซ่อน - พร้อมการประทับเวลาที่ดี & รวดเร็ว

จัดการช่องว่างในชื่อไฟล์ได้ดี - ไม่ใช่ว่าคุณควรใช้มัน!

$ 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

อื่น ๆfindมากมายสามารถพบได้โดยไปที่ลิงก์


3

ฉันกำลังแสดงสิ่งนี้สำหรับเวลาการเข้าถึงล่าสุดคุณสามารถแก้ไขสิ่งนี้เพื่อทำเวลา mod ล่าสุดได้อย่างง่ายดาย

มีสองวิธีในการทำสิ่งนี้:


1) หากคุณต้องการหลีกเลี่ยงการจัดเรียงทั่วโลกซึ่งอาจมีราคาแพงหากคุณมีไฟล์หลายสิบล้านไฟล์คุณสามารถทำได้: (วางตำแหน่งตัวเองในรากของไดเรกทอรีที่คุณต้องการให้การค้นหาของคุณเริ่มต้น)

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 

วิธีการด้านบนจะพิมพ์ชื่อไฟล์ด้วยเวลาในการเข้าถึงที่ใหม่กว่าและไฟล์สุดท้ายที่พิมพ์จะเป็นไฟล์ที่มีเวลาเข้าถึงล่าสุด คุณสามารถรับเวลาเข้าถึงล่าสุดได้อย่างชัดเจนโดยใช้ "tail -1"


2) คุณสามารถค้นหาซ้ำ ๆ ชื่อเข้าถึงเวลาของไฟล์ทั้งหมดในไดเรกทอรีย่อยของคุณแล้วเรียงตามเวลาในการเข้าถึงและท้ายรายการที่ใหญ่ที่สุด:

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

และคุณมีมัน ...


3

ฉันมีชื่อแทนนี้ใน. โปรไฟล์ของฉันที่ฉันใช้บ่อย

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

ดังนั้นมันจึงเป็นสิ่งที่คุณกำลังมองหา (ยกเว้นมันจะไม่เปลี่ยนเส้นทางวันที่ / เวลาหลายระดับ) - มองหาไฟล์ล่าสุด (ไฟล์ * .log และ * .trc ในกรณีนี้); นอกจากนี้มันจะค้นหาเฉพาะไฟล์ที่ถูกแก้ไขในวันที่แล้วเรียงลำดับตามเวลาและไปป์ที่เอาต์พุตผ่านทางน้อยกว่า:

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

PS สังเกตว่าฉันไม่มีรูทบนเซิร์ฟเวอร์บางตัว แต่มี sudo เสมอดังนั้นคุณอาจไม่ต้องการส่วนนั้น


สิ่งนี้เป็น "สิ่งที่คุณกำลังมองหา" ได้อย่างไร? OP เขียนคำอธิบายที่ดีเกี่ยวกับสิ่งที่เขาต้องการและสิ่งนี้ก็ไม่สนใจมันเลย
hmijail mourns ลาออกจากการทำงาน

ขอบคุณที่ชี้ไปที่ คุณถูกต้อง - วิธีนี้ไม่ได้มีหลายระดับเพื่อให้ได้วันที่ / เวลาเปลี่ยนมันจะแสดงเฉพาะวันที่ / เวลาของไฟล์ไดเรกทอรีที่อยู่ภายใน แก้ไขคำตอบของฉัน
Tagar

1

คุณสามารถลองพิมพ์คำสั่ง printf

เวลาเข้าถึงล่าสุดของไฟล์ Ak ในรูปแบบที่ระบุโดย k ซึ่งเป็น@' or a directive for the C ฟังก์ชัน strftime ' ค่าที่เป็นไปได้สำหรับ k แสดงอยู่ด้านล่าง บางระบบอาจไม่สามารถใช้ได้กับทุกระบบเนื่องจากความแตกต่างของ "strftime" ระหว่างระบบ


1

ฟังก์ชั่นทุบตีด่วน:

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

ค้นหาไฟล์ที่แก้ไขล่าสุดในไดเรกทอรี:

findLatestModifiedFiles "/home/jason/" 1

คุณยังสามารถระบุรูปแบบวันที่ / เวลาของคุณเองเป็นอาร์กิวเมนต์ที่สาม


1

ต่อไปนี้จะคืนค่าสตริงการประทับเวลาและชื่อของไฟล์ที่มีการประทับเวลาล่าสุดให้กับคุณ:

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

ส่งผลให้เอาต์พุตของฟอร์ม: <yy-mm-dd-hh-mm-ss.nanosec> <filename>


1

นี่คือเวอร์ชันหนึ่งที่ทำงานกับชื่อไฟล์ที่อาจมีช่องว่าง, บรรทัดใหม่, ตัวอักษรแบบกลมเช่นกัน:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf พิมพ์การแก้ไขไฟล์ (ค่า EPOCH) ตามด้วยเว้นวรรคและ \0ชื่อไฟล์ที่ถูกยกเลิก
  • sort -zk1nr อ่านข้อมูลที่ยกเลิก NUL และเรียงกลับเป็นตัวเลข

เป็นคำถามที่ติดแท็กด้วย Linux ดังนั้นฉันคิดว่า gnuมีอยู่

คุณสามารถส่งไปด้านบนด้วย:

xargs -0 printf "%s\n"

เพื่อพิมพ์เวลาแก้ไขและชื่อไฟล์ที่เรียงลำดับตามเวลาที่แก้ไข (ล่าสุดมาก่อน) สิ้นสุดด้วยบรรทัดใหม่


1

นี่คือสิ่งที่ฉันใช้ (มีประสิทธิภาพมาก):

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

ข้อดี:

  • มันวางไข่เพียง 3 กระบวนการ

การใช้:

find_last [dir [number]]

ที่อยู่:

  • dir - ไดเรกทอรีที่จะค้นหา [ปัจจุบัน dir]
  • number - จำนวนไฟล์ใหม่ล่าสุดที่จะแสดง [10]

ผลลัพธ์สำหรับfind_last /etc 4ลักษณะเช่นนี้:

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

สำหรับlsเอาต์พุตธรรมดาใช้สิ่งนี้ ไม่มีรายการอาร์กิวเมนต์ดังนั้นจึงไม่นานเกินไป:

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

และทำให้ดีขึ้นด้วยcutเพียงแค่วันที่เวลาและชื่อ:

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

แก้ไข : เพิ่งสังเกตว่าคำตอบยอดนิยมปัจจุบันเรียงลำดับตามวันที่แก้ไข นั่นเป็นเรื่องง่ายเหมือนตัวอย่างที่สองที่นี่เนื่องจากวันที่แก้ไขเป็นครั้งแรกในแต่ละบรรทัด - ตบเรียงลำดับไปยังจุดสิ้นสุด:

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

0

สิ่งนี้สามารถทำได้ด้วยฟังก์ชัน reccursive ใน bash ด้วย

ให้ F ฟังก์ชั่นที่แสดงเวลาของไฟล์ซึ่งต้องเรียงลำดับตามพจนานุกรม yyyy-mm-dd เป็นต้น (ขึ้นอยู่กับระบบปฏิบัติการ?)

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

R ฟังก์ชั่นวนซ้ำที่เรียกใช้ผ่านไดเรกทอรี

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

และในที่สุดก็

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.