Як знайти найстаріший файл у дереві каталогів


Відповіді:


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 зберігає дату створення inode, але вона не відкрита через будь-які системні виклики, і вам потрібно використовувати налагодження, щоб побачити її.)
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немає.

Тут викладено ще кілька варіантів вирішення цієї проблеми: Як я можу знайти останній (найновіший, найдавніший, найстаріший) файл у каталозі? - Вікі Грега


Це також працює, але також видає 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/[^ ]* //')"

Використання нульового байта ( \0) замість символу передачі рядка ( \n) гарантує, що вихід знаходження все ще буде зрозумілим у випадку, якщо одне з імен файлів містить символ передачі рядка.

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

Команди впорядковані за часом виконання (вимірюється на моїй машині).

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

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

  • Остання команда взагалі не здійснює перетворення і повинна бути значно швидшою, ніж перші дві. Сама команда find не відобразить mtime найстарішого файлу, тому stat потрібен.

Сторінки, що стосуються чоловіка: 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, то ПОЧАКУЙТЕ за допомогою нульового лічильника (необов’язково для цього питання) та встановивши найдавнішу дату, яку має бути сьогодні, формату YearMonthDay.
  • Основна петля спочатку
    • Захоплює 6-е поле, дату, формат року-місяця-дня та змінює його на YearMonthDay (якщо ваш ls не виводить таким чином, вам може знадобитися точна настройка).
    • Використовуючи рекурсивну форму, для всіх каталогів з'являться рядки заголовків у вигляді / каталогу / тут:. Захопіть цю лінію в змінну pat. (заміна останнього ":" на "/"). І встановлює $ 6 нічим, щоб уникнути використання рядка заголовка як дійсного рядка файлу.
    • якщо поле $ 6 має дійсне число, його дата. Порівняйте його зі старою датою.
    • Це старше? Потім збережіть нові значення для старої дати oldd та старої назви файлу oldf. До речі, oldf - це не тільки 8-е поле, але й 8-е до кінця. Ось чому цикл для об'єднання з 8-го в NF (кінець).
    • Порахуйте аванси на одиницю
    • END, надрукувавши результат

Запуск:

~ $ час ls -lRU "$ PWD" / * | awk etc.

Найдавніша дата: 19691231

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

Усього порівняно: 111438

реальні 0м1.135с

користувач 0m0.872s

sys 0m0.760s


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

EDIT 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 для файлів з пробілами не є хорошою ідеєю. Можливо, використовуючи find.
Д - р Беко

Просто запустіть його у всьому дереві "/". Проведений час: Всього порівняно: 585744 реальні користувачі 2m14.017s 0m8.181s sys 0m8.473s
Д-р Беко

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

4

Будь ласка, використовуйте ls - сторінка man повідомляє, як замовити каталог.

ls -clt | head -n 2

-N 2 значить, ви не отримаєте "загальний" у виході. Якщо ви хочете лише ім'я файлу.

ls -t | head -n 1

І якщо вам потрібен список у звичайному порядку (отримання найновішого файлу)

ls -tr | head -n 1

Набагато простіше, ніж використовувати пошук, набагато швидше та надійніше - не потрібно турбуватися про формати імен файлів. Він також повинен працювати майже на всіх системах.


6
Це працює лише в тому випадку, якщо файли знаходяться в одному каталозі, а моє питання стосується дерева директорій.
Маріус Гедмінас

2
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1

Це не працює належним чином, якщо є файли, старші 9 вересня 2001 року (1000000000 секунд з часу епохи Unix). Щоб увімкнути числове сортування, використовуйте sort -n.
Денніс

Це допомагає знайти мені файл, але важко зрозуміти, скільки йому років, не виконуючи другу команду :)
Marius Gedminas

0

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

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

Зауважте %A+.


-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

Що важко відповісти? знайти ./search/dirname -тип f -printf '% T +% h /% f \ n' | сортувати | head -n 1 Він показує два стовпці як час та шлях файлу. Потрібно видалити перший стовпець. Використовуючи набір і відлуння $ 2
Діма

1
Вам слід надати пояснення, а не просто вставляти командний рядок, як цього вимагають кілька інших користувачів.
Ob1lan

1
Чим це відрізняється від прийнятої відповіді?
Рамхаунд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.