Я шукаю однокласник оболонки, щоб знайти найстаріший файл у дереві каталогів.
Я шукаю однокласник оболонки, щоб знайти найстаріший файл у дереві каталогів.
Відповіді:
Це працює (оновлено, щоб включити пропозицію Даніеля Андерссона):
find -type f -printf '%T+ %p\n' | sort | head -n 1
find
порожній через те, що у мене ім'я файлу містить новий рядок.
Це трохи портативніше, тому що він не покладається на 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. Я поняття не маю, чому я не отримую подібної помилки, коли я передаю висновок сортування на голову в своєму рішенні.
head
команда, яка закриває, як тільки вона прочитає рядок і тим самим "зламає" трубу, я думаю. Ви не отримуєте помилку, оскільки sort
, схоже, не скаржиться на неї, але ls
в іншому випадку.
xargs
потрібно викликати ls
не один раз. У цьому випадку відсортовані результати цих кількох викликів в кінцевому підсумку з’єднуються, коли їх слід об'єднати.
ls
його, і очне яблуко найстаріший файл, ваше рішення, ймовірно , перевищить обмеження довжини командного рядка, викликаючи ls
його виклик кілька разів. Ви отримаєте неправильну відповідь, але ніколи не дізнаєтесь.
Наступні команди команд гарантовано працюють з будь-якими дивними іменами файлів:
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
Хоча прийнята відповідь та інші тут виконують роботу, якщо у вас дуже велике дерево, усі вони будуть сортувати цілу купу файлів.
Краще було б, якби ми могли просто перерахувати їх і відслідковувати найдавніші, без потреби взагалі сортувати.
Ось чому я придумав таке альтернативне рішення:
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 "$ 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
це добре для створення сценаріїв, але, можливо, також було б добре додати цю інформацію, перш ніж розповісти про ls
рішення.
Будь ласка, використовуйте ls - сторінка man повідомляє, як замовити каталог.
ls -clt | head -n 2
-N 2 значить, ви не отримаєте "загальний" у виході. Якщо ви хочете лише ім'я файлу.
ls -t | head -n 1
І якщо вам потрібен список у звичайному порядку (отримання найновішого файлу)
ls -tr | head -n 1
Набагато простіше, ніж використовувати пошук, набагато швидше та надійніше - не потрібно турбуватися про формати імен файлів. Він також повинен працювати майже на всіх системах.
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
sort -n
.
Здається, що під "найстарішими" більшість людей припускають, що ви мали на увазі "найдавніший час модифікації". Це, мабуть, виправлено відповідно до найсуворішої інтерпретації "найстарішого", але якби ви хотіли відповіді з найдавнішим часом доступу , я змінив би найкращу відповідь таким чином:
find -type f -printf '%A+ %p\n' | sort | head -n 1
Зауважте %A+
.
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
відображає другий стовпчик, тобто ім'я файлу.
find -type f -printf '%T+ %p\n' | sort | head -1