В оболочке, как я могу 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. При обработке любого списка путей к файлам важно использовать только нулевое значение в качестве разделителя и при передаче даже одного пути к файлу от одной программы к другой делать это таким образом, чтобы не задушить произвольные байты. Существует много, казалось бы, правильных способов решения этой и других проблем, которые терпят неудачу, предполагая (даже случайно), что в именах файлов не будет ни новых строк, ни пробелов. Ни одно предположение не является безопасным.
Для сегодняшних целей первый шаг - получить список файлов с нулевым разделением. Это довольно легко, если у вас есть find
поддержка, -print0
такая как GNU:
find . -print0
Но этот список еще не говорит нам, какой из них является новейшим, поэтому нам нужно включить эту информацию. Я решил использовать -printf
переключатель поиска, который позволяет мне указать, какие данные появляются в выводе. Не все версии find
поддержки -printf
(это не стандарт), но GNU find делает. Если вы окажетесь без -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
две вещи: во-первых, он должен рассматривать period ( .
) как разделитель полей и второе, что оно должно использовать только первое и второе поля при рассмотрении того, как сортировать записи.
| 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
не принимают переключатели, чтобы заставить их работать на вводе с нулевым разделением. Вместо этого я использую цикл while как вид бедного человека head
.
| while IFS= read -r -d '' record
Сначала я сбрасываю, IFS
чтобы список файлов не подвергался разбиению по словам. Далее я расскажу read
две вещи: не интерпретируйте escape-последовательности в input ( -r
), а вход ограничивается нулевым byte ( -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, а «Время последнего изменения статуса».
Таким образом, лучшее, что вы можете получить, это «привязать последний недавно измененный файл», что объясняется в других ответах. Я бы пошел для этой команды:
tail -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`
Биты между обратными чертами (кавычки, подобные символам) интерпретируются, и результат возвращается в tail.
ls -t #gets the list of files in time order
head -n 1 # returns the first line only
Простой:
tail -f /path/to/directory/*
у меня просто отлично работает
Проблема в том, чтобы получить файлы, сгенерированные после запуска команды tail. Но если вам это не нужно (поскольку все вышеперечисленные решения не заботятся об этом), звездочка - это просто более простое решение, IMO.
Кто-то опубликовал его, а затем по какой-то причине стер его, но это единственный, который работает, так что ...
tail -f `ls -tr | tail`
ls
не самая разумная вещь ...
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`
Объяснение: