Как я могу найти самый старый файл в дереве каталогов

72

Я ищу оболочку с одной строкой, чтобы найти самый старый файл в дереве каталогов.

Мариус Гедминас
источник

Ответы:

72

Это работает (обновлено, чтобы включить предложение Даниэля Андерссона):

find -type f -printf '%T+ %p\n' | sort | head -n 1
Мариус Гедминас
источник
8
Меньше печатать:find -type f -printf '%T+ %p\n' | sort | head -1
Даниэль Андерссон
1
Я получаю пустое место, потому что моя первая строка из этого findпуста из-за того, что у меня есть имя файла содержит новую строку .
皞 皞
1
Могу ли я спросить, использует ли это дату создания или изменения?
MrMesees
1
Linux нигде не хранит дату создания файла [*]. Это использует дату модификации. [*] это на самом деле не так; В ext4 хранится дата создания инода, но она не отображается ни через какие системные вызовы, и вам нужно использовать debugfs, чтобы увидеть ее.)
Marius Gedminas
11

Это немного более переносимо, и потому что оно не зависит от findрасширения GNU -printf, поэтому оно работает и на BSD / OS X:

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

Единственным недостатком здесь является то, что он несколько ограничен размером ARG_MAX(который не должен иметь значения для большинства новых ядер). Так что, если getconf ARG_MAXвозвращено больше символов (262 144 в моей системе), это не даст вам правильный результат. Это также не POSIX-совместимый, потому что -print0и xargs -0не.

Здесь описаны некоторые другие решения этой проблемы: Как найти самый последний (самый новый, самый ранний, самый старый) файл в каталоге? - Грег вики

slhck
источник
Это тоже работает, но также выдает xargs: ls: terminated by signal 13ошибку как побочный эффект. Я предполагаю, что это SIGPIPE. Я понятия не имею, почему я не получаю подобную ошибку, когда я направляю вывод сортировки в заголовок в моем решении.
Мариус Гедминас
Ваш вариант также легче набирать из памяти. :-)
Мариус Гедминас
Да, это сломанная труба. Я не получаю это как с версиями GNU, так и с BSD всех этих команд, но это headкоманда, которая завершает работу после того, как прочитала строку и, таким образом, «разрывает» канал, я думаю. Вы не получаете ошибку, потому sortчто, кажется, не жалуетесь на это, но lsв другом случае.
slhck
4
Это ломается, если существует так много имен файлов, которые xargsнужно вызывать lsболее одного раза. В этом случае отсортированные выходные данные этих нескольких вызовов в конечном итоге объединяются, когда они должны быть объединены.
Николь Гамильтон
2
Я думаю, что это хуже, чем публикация скрипта, который предполагает, что имена файлов никогда не содержат пробелов. В большинстве случаев они будут работать, потому что в именах файлов нет пробелов. И когда они терпят неудачу, вы получаете ошибку. Но это вряд ли сработает в реальных случаях, и неудача останется незамеченной. В любом дереве каталогов, достаточно большом, чтобы вы не могли просто lsего просмотреть и просмотреть самый старый файл, ваше решение, вероятно , превысит ограничение длины командной строки, вызывая lsего многократный вызов. Вы получите неправильный ответ, но никогда не узнаете.
Николь Гамильтон
11

Следующие команды гарантированно работают с любыми странными именами файлов:

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/[^ ]* //')"

Использование нулевого byte ( \0) вместо символа перевода строки ( \n) гарантирует, что вывод find будет по-прежнему понятен в случае, если одно из имен файлов содержит символ перевода строки.

-zПереключатель делает как - то и Grep интерпретировать только нулевые байты как отслужившую строки символов. Поскольку такого переключателя для головы нет, мы используем его grep -m 1(только одно вхождение).

Команды упорядочены по времени выполнения (измерено на моей машине).

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

  • Вторая команда немного быстрее. В то время как он все еще выполняет преобразование даты, численно сортировка ( sort -n) истекла, поскольку эпоха Unix немного быстрее. sed удаляет секунды, начиная с эпохи Unix.

  • Последняя команда не выполняет преобразования вообще и должна быть значительно быстрее, чем первые две. Сама команда find не будет отображать mtime самого старого файла, поэтому требуется статистика.

Связанные справочные страницы: find - grep - sed - sort - stat

Деннис
источник
5

Хотя принятый ответ и другие здесь делают свою работу, если у вас очень большое дерево, все они будут сортировать всю кучу файлов.

Лучше было бы, если бы мы могли просто перечислить их и отслеживать самые старые, без необходимости сортировки вообще.

Вот почему я придумал это альтернативное решение:

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

Я надеюсь, что это может помочь, даже если вопрос немного устарел.


Редактировать 1: эти изменения позволяют анализировать файлы и каталоги с пробелами. Он достаточно быстр, чтобы выдать его в корень /и найти самый старый файл.

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

Команда объяснила:

  • ls -lRU - time-style = long-iso "$ PWD" / * перечисляет все файлы (*), длинный формат (l), рекурсивно (R), без сортировки (U), чтобы быть быстрым, и направляет его в awk
  • Awk затем НАЧИНАЕТСЯ с обнуления счетчика (необязательно для этого вопроса) и установки самой старой даты oldd на сегодня, в формате YearMonthDay.
  • Основной цикл первым
    • Возьмите 6-е поле, дату, формат Год-Месяц-День и измените его на YearMonthDay (если ваш ls не выводит таким образом, вам может потребоваться его точная настройка).
    • Используя рекурсив, для всех каталогов появятся строки заголовка в виде / directory / here :. Захватите эту строку в переменную pat. (подставляя последний ":" в "/"). И устанавливает $ 6, чтобы избежать использования строки заголовка в качестве допустимой строки файла.
    • если поле $ 6 имеет действительное число, это дата. Сравните это со старой датой oldd.
    • Это старше? Затем сохраните новые значения для старой даты oldd и старого файла oldf. Кстати, oldf - это не только 8-е поле, но и с 8-го до конца. Вот почему цикл для конкатенации от 8-го до NF (конец).
    • Считать авансы на один
    • КОНЕЦ, напечатав результат

Запуск это:

~ $ time ls -lRU "$ PWD" / * | awk и т. д.

Самая старая дата: 19691231

Файл: /home/.../.../backupold/.../EXAMPLES/how-to-program.txt

Всего по сравнению: 111438

реальный 0m1.135s

пользователь 0m0.872s

sys 0m0,760s


РЕДАКТИРОВАТЬ 2: та же концепция, лучшее решение, использующее findдля просмотра времени доступа (используйте вместо %Tпервого printfдля времени модификации или %Cдля изменения статуса ).

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

РЕДАКТИРОВАТЬ 3: Команда ниже использует время модификации, а также печатает пошаговый прогресс при поиске старых и старых файлов, что полезно, если у вас есть неправильные метки времени (например, 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}'
Доктор Беко
источник
Это все еще нуждается в настройке, чтобы принять файлы с пробелами. Я сделаю это в ближайшее время.
Доктор Беко
Я думаю, что разбор ls для файлов с пробелами не очень хорошая идея. Может быть, с помощью поиска.
Доктор Беко
Просто запустите его во всем дереве "/". Потрачено времени: Всего по сравнению: 585744 реальный 2м14.017с пользователь 0м8.181с системный 0м8.473с
доктор Беко
Использование lsплохо для сценариев, так как его вывод не предназначен для машин, форматирование вывода варьируется в зависимости от реализации. Как вы уже сказали, findэто хорошо для написания сценариев, но также может быть полезно добавить эту информацию, прежде чем рассказывать о lsрешениях.
Сампо Саррала
4

Пожалуйста, используйте ls - страница руководства расскажет вам, как заказать каталог.

ls -clt | head -n 2

-N 2 означает, что вы не получите «итого» в выводе. Если вы хотите только имя файла.

ls -t | head -n 1

И если вам нужен список в обычном порядке (получение новейшего файла)

ls -tr | head -n 1

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

user1363990
источник
6
Это работает, только если файлы находятся в одном каталоге, в то время как мой вопрос был о дереве каталогов.
Мариус Гедминас
2
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
Okki
источник
Это не будет работать должным образом, если есть файлы старше 9 сентября 2001 года (1000000000 секунд с начала эпохи Unix). Чтобы включить числовую сортировку, используйте sort -n.
Деннис
Это помогает мне найти файл, но трудно понять, сколько ему лет без выполнения второй команды :)
Marius Gedminas
0

Кажется, что под «самым старым» большинство людей полагало, что вы имели в виду «самое старое время модификации». Это, вероятно, исправлено в соответствии с самым строгим толкованием «самого старого», но в случае, если вам нужен тот, у которого самое старое время доступа , я бы изменил лучший ответ следующим образом:

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

Обратите внимание %A+.

PenguinLust
источник
-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' печатает даты и имена файлов в двух столбцах.
  • sort | head -n1 сохраняет строку, соответствующую самому старому файлу.
  • echo $2 отображает второй столбец, т.е. имя файла.
Дима
источник
1
Добро пожаловать в Супер пользователя! Хотя это может дать ответ на вопрос, было бы лучше, если бы вы могли объяснить, почему это так.
DavidPostill
1
Обратите внимание, что несколько человек также попросили дать объяснение вашего предыдущего (идентичного) удаленного ответа.
DavidPostill
На что сложно ответить? find ./search/dirname -type f -printf '% T +% h /% f \ n' | сортировать | head -n 1 Показывает два столбца как время и путь к файлу. Надо убрать первый столбец. Использование set и echo $ 2
Дима
1
Вы должны предоставить объяснения, а не просто вставлять командную строку, как этого требуют несколько других пользователей.
Ob1lan
1
Чем это отличается от принятого ответа?
Ramhound