Найти последний файл по дате изменения

Если я хочу найти самый последний файл (mtime) в (большом) каталоге, содержащем подкаталоги, как мне это сделать?

Множество сообщений, которые я нашел, предлагают некоторые варианты ls -lt | head (забавно, многие предлагают ls -ltr | tail это то же самое, но менее эффективно), что хорошо, если у вас нет подкаталогов (у меня есть).

Опять же, вы могли бы

find . -type f -exec ls -lt \{\} \+ | head

который определенно сделает то же самое для столько файлов, сколько может быть указано одной командой, т.е. если у вас большой каталог, -exec...\+ будет выдавать отдельные команды; поэтому каждая группа будет отсортирована по ls внутри себя, но не над всей совокупностью; Таким образом, головка получит последнюю запись первой партии.

Есть ответы?

6 ответов

Решение

Вам не нужно возвращаться к внешним командам (как ls) так как find может сделать все, что вам нужно через -printf действие:

find /path -printf '%T+ %p\n' | sort -r | head

У меня была похожая проблема сегодня, но я атаковал ее без find, Мне нужно что-то короткое, я мог бы переехать ssh вернуть последний отредактированный файл в моем домашнем каталоге. Это примерно то, что я придумал:

ls -tp | grep -v /$ | head -1

-p возможность ls добавляет косую черту в каталогах, grep -v удаляет строки, заканчивающиеся косой чертой (или все каталоги), а head -1 ограничивает вывод одним файлом.

Это гораздо менее многословно, чем при использовании find если все, что вы хотите вернуть, это имя файла.

Это в моей системе быстрее, чем printfхотя я не понимаю почему

find /path -type f -exec stat -c "%y %n" {} + | sort -r | head

Использование perl в сочетании с find:

 find my_directory -type f -printf '%T@\t%p\n' | perl -ane '@m=@F if ($F[0]>$m[0]); END{print $m[1];}'

Вы получите имя файла с наибольшей эпохой == последний измененный файл.

РЕДАКТИРОВАТЬ: я думаю, что этот пост не "не особенно полезен", как я думал, что это было. Это действительно быстрое решение, которое просто отслеживает последний измененный файл (вместо сортировки всего списка файлов):

find . -type f -printf '%T@ %p\n' | awk 'BEGIN { mostrecenttime = 0; mostrecentline = "nothing"; } { if ($1 > mostrecenttime) { mostrecenttime = $1; mostrecentline = $0; } } END { print mostrecentline; }' | cut -f2- -d ' '

Для ясности распределите его по нескольким строкам:

find . -type f -printf '%T@ %p\n' | awk '
    BEGIN { mostrecenttime = 0; mostrecentline = "nothing"; }
    {
        if ($1 > mostrecenttime)
            { mostrecenttime = $1; mostrecentline = $0; }
    }
    END { print mostrecentline; }' | cut -f2- -d ' '

Конец РЕДАКТИРОВАНИЯ


Не очень полезный пост, но так как "аранжировка" обсуждала скорость, я решил поделиться этим.

Решения упорядочения и enzotib включают перечисление всех файлов в каталоге с их mtimes, а затем сортировку. Как известно, сортировка не нужна, чтобы найти максимум. Найти максимум можно за линейное время, но сортировка занимает n log (n) времени [я знаю, разница невелика, но все же;)]. Я не могу придумать изящный способ реализации этого. [РЕДАКТИРОВАТЬ: аккуратный (хотя грязный вид) и быстрая реализация, представленная выше.]

Следующая лучшая вещь - чтобы найти последний отредактированный файл в каталоге, рекурсивно найдите самый последний отредактированный файл в каждом подкаталоге уровня 1. Пусть этот файл представляет подкаталог. Теперь сортируйте файлы уровня 1 вместе с представителями подкаталогов уровня 1. Если количество файлов уровня 1 и подкаталогов в каждом каталоге почти постоянное, то этот процесс должен линейно масштабироваться с общим количеством файлов.

Вот что я придумал, чтобы реализовать это:

findrecent() { { find "$1" -maxdepth 1 -type f -exec stat -c "%y %n" {} + | sort -r | head -1 && find "$1" -mindepth 1 -maxdepth 1 -type d -exec findrecent {} \;; } | sort -r | head -1; }
findrecent .

Я запустил это и получил кучу find: findrecent: No such file or directory ошибки. Причина: -exec команды find выполняется в другой оболочке. Я попытался определить findrecent в.bashrc, .xsessionrc, но это не помогло [я был бы признателен за помощь здесь]. В конце концов я прибегнул к

#!/bin/bash
{ find "$1" -maxdepth 1 -type f -exec stat -c "%y %n" {} + | sort -r | head -1 && find "$1" -mindepth 1 -maxdepth 1 -type d -exec findrecent {} \;; } | sort -r | head -1;

в сценарии под названием findrecent в моем пути и затем запустить его.

Я запустил это, продолжал ждать и ждать без выхода. Просто чтобы убедиться, что я не имел дело с бесконечными циклами, я изменил файл

#!/bin/bash
echo "$1" >&2
{ find "$1" -maxdepth 1 -type f -exec stat -c "%y %n" {} + | sort -r | head -1 && find "$1" -mindepth 1 -maxdepth 1 -type d -exec findrecent {} \;; } | sort -r | head -1;

и попробовал еще раз. Это сработало - но заняло 1 минуту 35 секунд в моей домашней папке - решения аранжировки и энзотиба заняли 1,69 и 1,95 секунды соответственно!

Вот вам и превосходство O(n) над O(n log (n))! Черт возьми, функция вызова накладных расходов! [Или скорее сценарий вызова накладных расходов]

Но этот скрипт масштабируется лучше, чем предыдущие решения, и я уверен, что он будет работать быстрее, чем они в банке памяти Google;D

Это не так модно, но этого можно добиться и с Midnight Commander: искать *, группировать результаты, сортировать по времени изменения в обратном порядке.

Очевидно, это немного медленнее, чем find - мой домашний каталог, содержащий 922000 файлов, был отсортирован по mc почти за 14 минут find потратил меньше 5 - но есть некоторые преимущества:

  • Вероятно, я бы потратил больше, чем разница в 9 минут, придумывая правильный вызов find:)

  • меньше шансов на ошибку (забыл указать -r для сортировки и т. д. - начать заново)

  • можно поиграть с результирующим набором, изменив порядок сортировки и т. д. - без повторного запроса файлов.

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

Другие вопросы по тегам