В оболочке, как я могу 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 " "`
Объяснение: