Как рекурсивно найти и вывести список последних измененных файлов в каталоге с подкаталогами и временем?


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 ответов, и большинство / все даже не пытаются делать то, что указано ОП ...
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делает работу. Это тоже немного быстрее.

4
мы можем как-то превратить комментарий @ user37078 в реальный ответ или отредактировать оригинальный ответ? кажется, что это «правильный путь» [тм].
Мнагель

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 найти.
Сабунку

очень лаконично очень хорошо!
Рэнди Л

это быстрее, чем другие решения, более сложные
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чтобы предотвратить вывод тысяч строк в выводе.
февраля

8
Другой комментарий: awk '{print $2}'кажется, что часть вызывает проблемы, когда есть имена файлов с пробелами. Вот решение, использующее sedвместо этого, и оно также печатает время в дополнение к пути:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
SFFC

3
Я думаю, что это должно быть что-то вроде
Боян Девич

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 # * ""} хорошо сработали для меня (дайте мне материал до первого пробела)
Дэвид Гудвин

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, решают эту проблему.
Карл Вербиест

@carlverbiest действительно, большое количество файлов сломает решение slashdottir. Даже решения на основе xargs будут медленными. Решение user2570243 лучше всего подходит для больших файловых систем.
Стефан Гурихон

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

17

Попробуй это

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

Он использует findдля сбора всех файлов из каталога,ls для их сортировки по дате изменения, headдля выбора 1-го файла и, наконец, statдля отображения времени в хорошем формате.

В настоящее время это небезопасно для файлов с пробелами или другими специальными символами в их именах. Напишите комментарий, если он еще не соответствует вашим потребностям.


1
Halo: Мне нравится ваш ответ, он хорошо работает и печатает правильный файл. Однако я не помогаю, так как в моем случае слишком много подуровней. Поэтому я получаю «Список аргументов слишком длинный» для ls ... и xargs в этом случае тоже не поможет. Я попробую что-нибудь еще.
Фредрик

В этом случае это немного сложнее и потребует какой-то реальной программы. Я взломаю немного Perl.
Даниэль Бёмер

1
Я решил это, используя вместо этого PHP. Рекурсивная функция, которая спускается по дереву файловой системы и сохраняет время последнего измененного файла.
Фредрик

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, а не от 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 Йосемити; Я получаю сообщение об ошибке: найти: ftsopen: нет такого файла или каталога
Рис

Интересно. Какую команду вы набрали (с параметрами)? И как назывались файлы в этом каталоге? И если вы создали свою собственную версию ~/bin/ls-recent.sh, тщательно ли вы проверили сценарий на наличие различий?
Джим 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-util .

Цитата из поста:

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

Игнорирование скрытых файлов - с хорошей и быстрой отметкой времени

Хорошо обрабатывает пробелы в именах файлов - не то чтобы вы их использовали!

$ 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); Кроме того, он находит только файлы, измененные в последний день, затем сортирует по времени и передает данные через less:

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

пс. Обратите внимание, что у меня нет root на некоторых серверах, но всегда есть sudo, поэтому вам может не понадобиться эта часть.


Как это «именно то, что вы ищете»? ОП написал хорошее объяснение того, что он хотел, и это полностью игнорирует это.
hmijail скорбит по отставке

спасибо за указание на это. вы правы - этот метод не проходит несколько уровней, чтобы получить дату и время изменения, он только показывает дату / время файлов каталогов внутри него. отредактировал мой ответ.
Тагар

1

Вы можете дать команду printf найти попытку

% Ak Время последнего доступа к файлу в формате, заданном параметром k, который является либо @' or a directive for the C функцией strftime. Возможные значения для k перечислены ниже; некоторые из них могут быть недоступны во всех системах из-за различий в strftime между системами.


1

Быстрая функция bash:

# 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 - каталог для поиска [текущий каталог]
  • 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

Это также можно сделать с помощью рекурсивной функции в bash.

Пусть F функция, которая отображает время файла, которое должно быть лексикографически сортируемым гггг-мм-дд и т. Д., (Зависит от ОС?)

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.