Як я можу tailотримати останній файл, створений у каталозі, в оболонці ?
Як я можу tailотримати останній файл, створений у каталозі, в оболонці ?
Відповіді:
Ви НЕ розібрати висновок Ls! Розбір результатів ls є складним і ненадійним .
Якщо ви повинні зробити це, я рекомендую використовувати find. Спочатку у мене був простий приклад, щоб просто дати вам суть рішення, але оскільки ця відповідь здається дещо популярною, я вирішив переглянути цю проблему, щоб надати версію, яку можна безпечно копіювати / вставляти та використовувати з усіма вхідними даними. Ви зручно сидите? Ми почнемо з oneliner, який дасть вам останній файл у поточному каталозі:
tail -- "$(find . -maxdepth 1 -type f -printf '%T@.%p\0' | sort -znr -t. -k1,2 | while IFS= read -r -d '' -r record ; do printf '%s' "$record" | cut -d. -f3- ; break ; done)"
Зараз не зовсім онлінер, чи не так? Ось він знову як функція оболонки та відформатований для легшого читання:
latest-file-in-directory () {
find "${@:-.}" -maxdepth 1 -type f -printf '%T@.%p\0' | \
sort -znr -t. -k1,2 | \
while IFS= read -r -d '' -r record ; do
printf '%s' "$record" | cut -d. -f3-
break
done
}
А тепер це як онлайнер:
tail -- "$(latest-file-in-directory)"
Якщо все інше не вдається, ви можете включити вищевказану функцію у свою програму .bashrcі вважати проблему вирішеною за допомогою одного застереження. Якщо ви просто хотіли виконати роботу, не потрібно читати далі.
Застереження в цьому полягає в тому, що ім'я файлу, що закінчується в одному або декількох нових рядках, все ще не буде передано tailналежним чином. Вирішити цю проблему складно, і я вважаю достатньою, що якщо зустрінеться таке зловмисне ім'я файлу, відносно безпечна поведінка зіткнення з помилкою "Немає такого файлу" стане замість чогось більш небезпечного.
Для допитливих це набридливе пояснення того, як це працює, чому це безпечно і чому інші методи, ймовірно, не є.
Перш за все, єдиний байт, який безпечно розмежовувати до файлових шляхів, є нульовим, оскільки це єдиний байт, який загально заборонений у шляху до файлів у системах Unix. Важливо при обробці будь-якого списку шляхів файлів використовувати лише null як роздільник, і, передаючи навіть один шлях до файлу від однієї програми до іншої, робити це таким чином, що не задихається довільних байтів. Існує багато начебто правильних способів вирішити цю та інші проблеми, які провалюються, якщо припустити (навіть випадково), що імена файлів не матимуть ні нових рядків, ні пробілів. Жодне припущення не є безпечним.
Для сьогоднішніх цілей першим кроком є вилучення з нуля списку файлів. Це досить просто, якщо у вас є така findпідтримка -print0, як GNU:
find . -print0
Але цей перелік все ще не каже нам, який із них є новітнім, тому нам потрібно включити цю інформацію. Я вибираю використовувати -printfперемикач find, який дозволяє мені вказати, які дані відображаються у висновку. Не всі версії findпідтримки -printf(це не стандартно), але GNU знайде. Якщо ви опинитеся без -printfвас, вам не доведеться покладатися на те, -exec stat {} \;в який момент ви повинні відмовитись від усієї надії на портативність, як statце теж не є стандартом. Поки що я буду переходити до того, що у вас є інструменти GNU.
find . -printf '%T@.%p\0'
Тут я прошу формату printf, %T@який є часом модифікації в секундах від початку епохи Unix, а потім періодом, а потім числом, що вказує на частки секунди. Я додаю до цього ще один період, а потім %p(який є повним шляхом до файлу), перш ніж закінчуватися нульовим байтом.
Тепер я
find . -maxdepth 1 \! -type d -printf '%T@.%p\0'
Само собою зрозуміло, але заради повного -maxdepth 1перешкоджання findперераховувати вміст підкаталогів та \! -type dпропускати каталоги, які ви навряд чи захочете tail. Поки у мене є файли в поточному каталозі з інформацією про час модифікації, тому тепер мені потрібно сортувати за тим часом модифікації.
За замовчуванням sortочікує, що його вхід буде записаний у новий рядок. Якщо у вас є GNU, sortви можете попросити його очікувати замість нульових записів замість цього за допомогою -zперемикача; для стандарту sortнемає рішення. Мене цікавить лише сортування за першими двома числами (секундами та частками секунди), і я не хочу сортувати за фактичним іменем файлу, тому я скажу sortдві речі: По-перше, він повинен враховувати період ( .) роздільник поля по-друге, він повинен використовувати лише перше та друге поля лише при розгляді питання сортування записів.
| sort -znr -t. -k1,2
Перш за все, я поєдную три короткі варіанти, які не мають значення разом; -znrце лише стислий спосіб сказати -z -n -r). Після цього -t .(пробіл необов’язковий) вказується sortсимвол роздільника поля та -k 1,2вказуються номери полів: перше та друге ( sortрахує поля від одного, а не до нуля). Пам'ятайте, що зразок запису для поточного каталогу виглядатиме так:
1000000000.0000000000../some-file-name
Це означає sortспочатку розглядати, 1000000000а потім 0000000000при замовленні цього запису. -nОпція вказує sortвикористовувати чисельну порівняння при зіставленні цих значень, оскільки обидва значення числа. Це може не важливо, оскільки номери мають фіксовану довжину, але це не шкодить.
Інший перемикач sortпризначений -rдля "реверсу". За замовчуванням вихід чисельного сортування буде спочатку найменшими числами, -rзмінює його так, щоб він перераховував найнижчі числа останніх та найвищі числа спочатку. Оскільки ці цифри тимчасові мітки вище, це означатиме новіші, і це ставить найновіший запис на початку списку.
По мірі того, як список шляхів до файлів виходить з sortнього, тепер є потрібна відповідь, яку ми шукаємо прямо вгорі. Залишилося знайти спосіб відкинути інші записи та зняти часову позначку. На жаль, навіть GNU headі tailне приймають комутатори, щоб змусити їх працювати на введенні з нульовим входом. Натомість я використовую цикл часу як різновид бідолахи head.
| while IFS= read -r -d '' record
Спочатку я скидаю, IFSщоб список файлів не піддавався поділу слів. Далі я розповім readдві речі: не інтерпретувати послідовності вхідних даних у вході ( -r), а вхід розмежований нульовим байтом ( -d); тут порожній рядок ''використовується для вказівки "немає роздільника", який також обмежений нулем. Кожен запис буде прочитаний у змінну, recordщоб кожен раз, коли whileцикл повторювався, він мав одну часову позначку та одне ім'я файлу. Зауважте, що -dце розширення GNU; якщо у вас є тільки стандарт, readця техніка не буде працювати і у вас є мало призову.
Ми знаємо, що recordзмінна має три частини до неї, усі розмежовані символами періоду. За допомогою cutутиліти можна витягти їх частину.
printf '%s' "$record" | cut -d. -f3-
Тут весь запис передається туди, printfа звідти трубопроводом cut; в Баш ви могли б спростити це далі , використовуючи тут рядок для cut -d. -3f- <<<"$record"для кращої продуктивності. Ми розповідаємо cutдві речі: Спочатку -dце повинен бути конкретний роздільник для ідентифікації полів (як і sortдля роздільника .). Друге cutдоручається -fдрукувати лише значення з певних полів; список полів подається у вигляді діапазону, 3-який вказує значення з третього поля та з усіх наступних полів. Це означає, що cutбуде читати та ігнорувати все, що стосується, включаючи друге, .яке воно знайде у записі, а потім надрукує решту, яка є частиною шляху до файлу.
Надрукувавши найновіший шлях до файлу, продовжувати продовжувати не потрібно: breakвиходить з циклу, не даючи йому перейти до другого шляху файлу.
Єдине, що залишається - це запущений tailшлях до файлу, повернутий цим конвеєром. Можливо, ви помітили в моєму прикладі, що я це зробив, уклавши трубопровід у підгрунтя; те, що ви, можливо, не помічали, - це те, що я вклав подшипник у подвійні лапки. Це важливо, оскільки, нарешті, навіть з усіма цим зусиллям, щоб бути безпечним для будь-яких імен файлів, нерозмінене розширення додаткової оболонки все ще може порушити справи. Більш детальне пояснення є , якщо ви зацікавлені. Другий важливий, але легко забуваючий аспект викликання, - tailце те, що я надав йому можливість --перед розширенням імені файлу. Це настановитьtailщо більше жодних параметрів не вказується, і все, що випливає, - це ім'я файлу, що дозволяє безпечно обробляти імена файлів, які починаються з -.
tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
headі tailне приймають комутатори, щоб змусити їх працювати на введенні з нульовим входом." Моя заміна head: … | grep -zm <number> "".
tail `ls -t | head -1`
Якщо ви переживаєте за назви файлів із пробілами,
tail "`ls -t | head -1`"
Ви можете використовувати:
tail $(ls -1t | head -1)
$()Конструкція починає подоболочкі , яка запускає команду ls -1t(лістинг всіх файлів в порядку часу, по одному в рядку) і трубопроводів , що через , head -1щоб отримати перший рядок (файл).
Вихід цієї команди (найновіший файл) потім передається для tailобробки.
Пам'ятайте, що це ризикує отримати каталог, якщо це остання створена запис каталогу. Я використовував цей трюк в псевдонімі, щоб редагувати останній файл журналу (з обертового набору) в каталозі, який містив лише ці файли журналу.
-1не потрібно, lsробить це для вас, коли він знаходиться в трубі. Порівняйте lsі ls|cat, наприклад.
У системах POSIX немає можливості отримати "останній створений" запис каталогу. Кожен запис каталогу має atime, mtimeі ctime, але в відміну від Microsoft Windows, то ctimeне означає , що CreationTime, але «Час останньої зміни статусу».
Тож найкраще, що ви можете отримати, це "виправити останній нещодавно змінений файл", що пояснено в інших відповідях. Я б пішов на цю команду:
хвіст -f "$ (ls -tr | sed 1q)"
Зверніть увагу на лапки навколо lsкоманди. Це змушує фрагмент працювати майже з усіма іменами файлів.
В zsh:
tail *(.om[1])
Дивіться: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers , тут mпозначається час модифікації m[Mwhms][-|+]n, а попередній oозначає, що він сортується одним способом ( Oсортує його іншим способом). В .означає тільки регулярні файли. У дужках [1]вибирається перший предмет. Вибрати три використання [1,3], щоб отримати найдавніше використання [-1].
Це добре короткий і не використовує ls.
Напевно, є мільйон способів це зробити, але спосіб, як я це зробив би, це такий:
tail `ls -t | head -n 1`
Біти між задніми позначками (цитата як символи) інтерпретуються, а результат повертається в хвіст.
ls -t #gets the list of files in time order
head -n 1 # returns the first line only
Простий:
tail -f /path/to/directory/*
працює для мене просто чудово.
Проблема полягає в тому, щоб отримати файли, що генеруються після запуску хвостової команди. Але якщо вам це не потрібно (оскільки всі рішення, наведені вище, це не стосується), зірочка - це просто просте рішення, IMO.
Хтось розмістив його, а потім чомусь стерв його, але це єдине, що працює, тож ...
tail -f `ls -tr | tail`
lsне є найрозумнішим ділом ...
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`
Пояснення:
tail "$(ls -1tr|tail -1)"