Як я можу 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)"