Як рекурсивно знайти та перелічити останні модифіковані файли в каталозі з підкаталогами та часом?


417
  • Операційна система: Linux

  • Тип файлової системи: ext3

  • Краще рішення: bash (скрипт / oneliner), рубін, пітон

У мене є кілька каталогів з кількома підкаталогами та файлами в них. Мені потрібно скласти список усіх цих каталогів, який побудований таким чином, що кожен каталог першого рівня відображається поруч із датою та часом останнього створеного / модифікованого файлу в ньому.

Щоб уточнити, якщо я торкнуся файла або зміню його вміст на кілька рівнів підкаталогів вниз, ця часова мітка повинна відображатися поруч із назвою каталогу першого рівня. Скажіть, у мене такий структурований каталог:

./alfa/beta/gamma/example.txt

і я змінюю вміст файлу example.txt, мені потрібно, щоб цей час відображався поруч із каталогом першого рівня alfaу читаному для людини вигляді, а не епоха. Я пробував деякі речі , використовуючи знахідку, xargs, sortі любить , але я не можу обійти цю проблему , що файлова система Відмітка «Альфа» не змінюється , коли я створюю / змінювати файли на кілька рівнів вниз.


Якщо ви можете відчути біль від його створення, можна використовувати github.com/shadkam/recentmost .
користувач3392225

4
Неймовірно. 16 відповідей, а більшість / усі навіть не намагаються зробити те, що вказано в ОП ...
hmijail сумує у відставці

Замість таких рішень, як перемикач -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 для підтримки пробілів і навіть каналів ліній у назви файлів. Ось що я використовую. find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head Це все ще вдається бути швидким для мене.
День

20
У Mac OS X це не стан GNU, тому команда не працює. Ви повинні brew install coreutilsвикористовувати і gstatзамість цьогоstat
CharlesB

36
Вам не потрібно бігати, statоскільки find PATH -type f -printf "%T@ %p\n"| sort -nrвиконує роботу. Це також трохи швидше.
NR

4
чи можемо ми якось перетворити коментар @ user37078 у фактичну відповідь чи змінити оригінальну відповідь? здається, це "правильний шлях" [тм] зробити це.
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.
Сабунку

дуже лаконічний. дуже хороший!
Ренді Л

це швидше, ніж інші складніші рішення
david.perez

20
Дійсно добре, також ви можете використовувати "find -ctime -50", наприклад, за останні 50 днів змін.
Горкем

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

2
Варіант -printf набагато швидший, ніж щоразу викликати процес "stat" - це скорочує години роботи з моїх резервних копій. Дякую за те, що мені це відомо. Я уникав речі awk / sed, оскільки мене турбує лише останнє оновлення в дереві - так X = $ (find / path -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"для створення такого файлу. Я думаю, що дизайн утиліт Unix має великі вади щодо назви файлів.
фрукти

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 $ (find. -type f))
slashdottir

3
Це не спрацює, якщо у вас дуже велика кількість файлів. відповіді, які використовують xargs, вирішують цю межу.
carl verbiest

@carlverbiest дійсно велика кількість файлів порушить рішення slashdottir. Навіть рішення на основі xargs будуть повільними. рішення користувача 2570243 найкраще для великих файлових систем.
Стефан Гурішон

IFS=$'\n'ні в якому разі не безпечно при обробці імен файлів: Нові рядки є дійсними символами у файлах файлів UNIX. Тільки символ NUL гарантовано не буде присутній на шляху.
Чарльз Даффі

17

Спробуйте це

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

Він використовує findдля збору всіх файлів з каталогу, lsсписку їх, відсортованих за датою модифікації, headдля вибору першого файлу та, нарешті, statдля показу часу у гарному форматі.

Наразі це не безпечно для файлів з пробілами або іншими спеціальними символами у своїх іменах. Напишіть подяку, якщо вона ще не відповідає вашим потребам.


1
ореол: Мені подобається ваша відповідь, вона добре працює і виводить правильний файл. Однак мені це не допомагає, оскільки в моєму випадку занадто багато підрівнів. Тому я отримую "Аргументаційний список занадто довгий" для ls ... і xargs теж не допоможе в цьому випадку. Я спробую щось інше.
Фредрік

У цьому випадку це трохи складніше і знадобиться реальна програма. Я зламаю трохи Perl.
Даніель Бьомер

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, як попросили оригінальні афіші, використовуйте statзамість gstat.

Звичайно, ця відповідь є видатним рішенням користувача user37078 , що просувається від коментаря до повної відповіді. Я змішав уявлення CharlesB , щоб використовувати gstatна Mac OS X. До речі, я отримав Coreutils від MacPorts, а не домашньої мови .

Ось як я спакував це в просту команду ~/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 йосеміт; Я отримую помилку: find: ftsopen: Немає такого файлу чи каталогу
Reece

Цікаво. Яку команду ви ввели (з параметрами)? А як називались файли в цьому каталозі? І якщо ви створили власну версію програми ~/bin/ls-recent.sh, чи ретельно перевіряли сценарій на наявність відмінностей?
Jim DeLaHunt

10
для тих, хто не хоче нічого встановлювати на Mac OS X:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Джейк

5

І рішення perl, і Python в цій публікації допомогли мені вирішити цю проблему в Mac OS X: /unix/9247/how-to-list-files-sorted-by-modification-date-recursively -no-stat-command-avail .

Цитування з поста:

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

Я показую це для останнього часу доступу, ви можете легко змінити це, щоб зробити останній час моди.

Є два способи зробити це:


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 

Вищеописаний метод друкує назви файлів із поступово новим часом доступу, а останній файл, який він друкує, - це файл із останнім часом доступу. Очевидно, ви можете отримати останній час доступу, використовуючи "хвіст -1".


2) Ви можете знайти рекурсивно друкувати ім'я, час доступу всіх файлів у своєму підкаталозі, а потім сортувати, залежно від часу доступу та хвоста, найбільшого запису:

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

І ось у вас це є ...


3

У мене цей псевдонім є .profile, який я використовую досить часто

$ 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

пс. Зауважте, я не маю root на деяких серверах, але завжди є sudo, тому ця частина вам може не знадобиться.


Як це "саме те, що ви шукаєте"? ОП написав гарне пояснення того, чого він хотів, і це повністю ігнорує його.
hmijail сумує у відставці

дякую за вказівку на це. ви маєте рацію - цей метод не має декількох рівнів, щоб отримати дату / час зміни, він лише показує дату / час файлів каталогів у ньому. відредагував мою відповідь.
Тагар

1

Ви можете дати команду printf знайти спробу

% Останній час доступу до файлу у форматі, визначеному k, що є @' or a directive for the C функцією стропового переривання. Нижче наведено можливі значення k; деякі з них можуть бути недоступні у всіх системах через відмінності в «стропі» між системами.


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

Це можна зробити і з рекурсивною функцією в басі

Нехай F - функція, яка відображає час файлу, який повинен бути лексикографічно відсортований yyyy-mm-dd тощо, (os-залежний?)

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.