Я использую tail -f для мониторинга файла журнала, в который ведется активная запись. Когда в файл журнала записывается определенная строка, я хочу выйти из режима мониторинга и продолжить работу с остальным сценарием.
В настоящее время я использую:
tail -f logfile.log | grep -m 1 "Server Started"
Когда строка найдена, grep завершает работу, как и ожидалось, но мне нужно найти способ заставить команду tail выйти так, чтобы скрипт мог продолжаться.
tail
Умирает только на следующей строке. Попробуйте это:date > log; tail -f log | grep -m 1 trigger
и затем в другой оболочке:echo trigger >> log
и вы увидите выходные данныеtrigger
в первой оболочке, но без завершения команды. Затем попробуйте:date >> log
во второй оболочке и команда в первой оболочке прекратит работу. Но иногда это слишком поздно; мы хотим завершить работу, как только появится триггерная линия, а не после завершения строки после триггерной линии.tail
+grep -q
как ответ 00metheusОтветы:
Простой POSIX с одним вкладышем
Вот простая однострочная. Для этого не нужны специфичные для bash или не POSIX трюки, или даже именованный канал. Все, что вам действительно нужно, это отделить окончание
tail
отgrep
. Таким образом, послеgrep
завершения сценарий может продолжаться, даже если онtail
еще не завершен. Итак, этот простой метод доставит вас туда:grep
будет блокировать, пока не найдет строку, после чего он выйдет. Сделавtail
запуск из его собственной вложенной оболочки, мы можем поместить его в фоновом режиме, чтобы он работал независимо. Между тем основная оболочка может продолжить выполнение скрипта сразу послеgrep
выхода.tail
будет задерживаться в своей вложенной оболочке до тех пор, пока в лог-файл не будет записана следующая строка, а затем завершится (возможно, даже после завершения основного сценария). Суть в том, что конвейер больше не ожидаетtail
завершения, поэтому конвейер завершается сразу послеgrep
выхода.Некоторые незначительные изменения:
tail
позволяет начинать чтение с текущей последней строки файла журнала, если строка существует ранее в файле журнала.tail
указать -F, а не -f. Это не POSIX, но он позволяетtail
работать, даже если журнал вращается во время ожидания.grep
выход после первого появления, но без распечатки строки триггера. Также это POSIX, а не -m1.источник
tail
работу на заднем плане. Как бы вы захватилиtail
PID внутри фоновой вложенной оболочки и выставили ее в качестве основной оболочки? Я могу только предложить необязательный обходной путь, убив всеtail
процессы, связанные с сеансом , используяpkill -s 0 tail
.tail
завершится, как только он попытается записать в сломанный канал. Канал прервется, как толькоgrep
завершится, поэтому, как только онgrep
будет завершен,tail
он прекратит работу после того, как файл журнала получит еще одну строку в нем.tail -f
.Принятый ответ не работает для меня, плюс он сбивает с толку и меняет файл журнала.
Я использую что-то вроде этого:
Если строка журнала соответствует шаблону, уничтожьте
tail
запущенный этим сценарием.Примечание: если вы хотите также просмотреть вывод на экране, либо
| tee /dev/tty
передайте эхо-строку , либо протестируйте в цикле while.источник
pkill
не определено POSIX и не доступно везде.Если вы используете Bash (по крайней мере, но кажется, что он не определен POSIX, поэтому он может отсутствовать в некоторых оболочках), вы можете использовать синтаксис
Он работает почти так же, как уже упоминавшиеся решения FIFO, но гораздо проще в написании.
источник
SIGTERM
(Ctrl + C, команду выхода или убейте его)tail
прочитает его, попытается вывести его, а затем получит SIGPIPE, который прекратит его. Итак, в принципе вы правы; ониtail
могут работать бесконечно, если в файл журнала никогда ничего не записывается. На практике это может быть очень аккуратным решением для многих людей.Есть несколько способов добраться
tail
до выхода:Плохой подход: заставить
tail
написать еще одну строкуВы можете принудительно
tail
написать еще одну строку вывода сразу после того,grep
как нашли совпадение и вышли. Это приведетtail
к томуSIGPIPE
, что он получит выход. Один из способов сделать это - изменить файл, отслеживаемыйtail
послеgrep
выхода.Вот пример кода:
В этом примере
cat
не завершит работу, покаgrep
не закроет свой стандартный вывод, поэтомуtail
вряд ли сможет записать в канал до того, как сможетgrep
закрыть свой стандартный вывод.cat
используется для распространения стандартного выводаgrep
неизмененного.Этот подход относительно прост, но есть несколько недостатков:
grep
закрыть stdout перед закрытием stdin, всегда будет условие гонки:grep
закрытие stdout, запускcat
для выхода, запускecho
, запускtail
для вывода строки. Если эта строка отправлена до того,grep
какgrep
она успела закрыть стандартный ввод,tail
она не будет получена, пока не будетSIGPIPE
записана другая строка.tail
- он не будет работать с другими программами.bash
«sPIPESTATUS
массив). Это не имеет большого значения в этом случае, потомуgrep
что всегда будет возвращать 0, но в целом средняя стадия может быть заменена другой командой, код возврата которой вам небезразличен (например, что-то, что возвращает 0, когда обнаружен «сервер запущен», 1 когда "сервер не запустился" обнаружено).Следующие подходы позволяют избежать этих ограничений.
Лучший подход: избегайте трубопроводов
Вы можете использовать FIFO, чтобы полностью избежать конвейера, позволяя продолжить выполнение после
grep
возврата. Например:Строки, отмеченные комментарием,
# optional
могут быть удалены, и программа все равно будет работать;tail
будет просто задерживаться, пока не прочитает другую строку ввода или не будет уничтожен каким-либо другим процессом.Преимущества этого подхода:
tail
grep
(или любую другую альтернативную команду, которую вы используете)Недостатком этого подхода является сложность, особенно управление FIFO: вам нужно будет безопасно сгенерировать временное имя файла, и вам нужно будет убедиться, что временный FIFO удален, даже если пользователь нажимает Ctrl-C в середине сценарий. Это можно сделать с помощью ловушки.
Альтернативный подход: отправить сообщение в Kill
tail
Вы можете получить выход из
tail
этапа конвейера, отправив ему сигнал типаSIGTERM
. Задача состоит в том, чтобы точно знать две вещи в одном и том же месте в коде:tail
PID и был лиgrep
выход.При использовании конвейерного типа
tail -f ... | grep ...
легко изменить первый этап конвейера, чтобы сохранитьtail
PID в переменной с помощью фонового изображенияtail
и чтения$!
. Также легко изменить второй этап конвейера для запускаkill
приgrep
выходе. Проблема заключается в том, что два этапа конвейера работают в отдельных «средах выполнения» (в терминологии стандарта POSIX), поэтому второй этап конвейера не может считывать переменные, установленные первым этапом конвейера. Без использования переменных оболочки либо второй этап должен каким-то образом вычислятьtail
PID, чтобы он мог завершитьсяtail
приgrep
возврате, либо первый этап должен быть каким-то образом уведомлен приgrep
возврате.Второй этап можно использовать
pgrep
для полученияtail
PID, но это будет ненадежно (вы могли бы соответствовать неправильному процессу) и непереносимо (pgrep
не указано в стандарте POSIX).Первый этап может отправить PID на второй этап через канал,
echo
используя PID, но эта строка будет смешана сtail
выходными данными. Демультиплексирование двух может потребовать сложной схемы экранирования, в зависимости от выводаtail
.Вы можете использовать FIFO, чтобы второй этап конвейера уведомлял первый этап конвейера при
grep
выходе. Тогда первый этап может убитьtail
. Вот пример кода:Этот подход имеет все плюсы и минусы предыдущего подхода, за исключением того, что он более сложный.
Предупреждение о буферизации
POSIX позволяет полностью буферизовать потоки stdin и stdout, что означает, что
tail
выходные данные могут не обрабатыватьсяgrep
сколь угодно долго. В системах GNU не должно быть никаких проблем: GNUgrep
используетread()
, что исключает любую буферизацию, а GNUtail -f
делает регулярные вызовыfflush()
при записи в stdout. В системах без GNU может потребоваться сделать что-то особенное, чтобы отключить или регулярно очищать буферы.источник
tail -f
Будет выводить только последние десять строк, а затем все следующие. Чтобы улучшить это, вы можете добавить опцию-n 10000
к хвосту, чтобы также выдавались последние 10000 строк.tail -f
через ФИФО и на нем оглавлению:mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
.tail -f log
запись в FIFO приведет к тому, что некоторые системы (например, GNU / Linux) будут использовать блочную буферизацию вместо строчной буферизации, что означает, чтоgrep
может не увидеть совпадающую строку, когда она появляется в журнале. Система может предоставить утилиту для изменения буферизации, например,stdbuf
из GNU coreutils. Однако такая утилита была бы непереносимой.grep -q -m 1 trigger <(tail -f log)
предложению в другом месте и согласен с тем фактом, чтоtail
в фоновом режиме он работает на одну строку длиннее, чем нужно.Позвольте мне расширить ответ @ 00promeheus (который является лучшим).
Может быть, вы должны использовать тайм-аут, а не ждать бесконечно.
Приведенная ниже функция bash будет блокироваться до тех пор, пока не появится заданное условие поиска или не истечет заданное время ожидания.
Статус выхода будет 0, если строка найдена в течение времени ожидания.
Возможно, файл журнала еще не существует сразу после запуска вашего сервера. В этом случае вам следует подождать, пока он появится, прежде чем искать строку:
Вот как вы можете использовать это:
источник
timeout
команда?timeout
- это единственный надежный способ не зависать бесконечно в ожидании сервера, который не может запуститься и уже вышел.Итак, после некоторого тестирования, я нашел быстрый способ с 1 строкой сделать эту работу. Похоже, tail -f выйдет, когда выйдет grep, но есть одна загвоздка. Кажется, он срабатывает только в том случае, если файл открыт и закрыт. Я сделал это, добавив пустую строку в файл, когда grep найдет совпадение.
Я не уверен, почему открытие / закрытие файла вызывает хвост, чтобы понять, что канал закрыт, поэтому я бы не стал полагаться на это поведение. но, похоже, сейчас работает.
Причина, по которой он закрывается, посмотрите на флаг -F, а не на флаг -f.
источник
tail
к выводу другой строки, но к томуgrep
времени завершилось (возможно, там есть условие гонки). Еслиgrep
вышел к тому времени,tail
пишет другую строку,tail
получитSIGPIPE
. Это приводитtail
к немедленному выходу.tail
(6), вы не можете легко настроить его поведение по-разному в зависимости от разных совпадений строк («сервер запущен» и «сервер не удалось запустить»), потому что вы не можете легко получить код возврата средней очереди газопровода. Существует альтернативный подход, который позволяет избежать всех этих проблем - см. Мой ответ.В настоящее время, как указано, все
tail -f
решения, представленные здесь, рискуют подхватить ранее зарегистрированную строку «Сервер запущен» (что может или не может быть проблемой в вашем конкретном случае, в зависимости от количества зарегистрированных строк и ротации файла журнала / усечение).Вместо того, чтобы чрезмерно усложнять вещи, просто используйте умнее
tail
, как показал bmike с фрагментом perl. Самое простое решение - этоretail
встроенная поддержка регулярных выражений с шаблонами условий запуска и остановки :Это будет следовать за файлом, как обычно,
tail -f
пока не появится первый новый экземпляр этой строки, а затем завершится. (-u
Опция не срабатывает на существующие строки в последних 10 строках файла в обычном режиме «Follow».)Если вы используете GNU
tail
(из coreutils ), следующий простейший вариант - использовать--pid
FIFO (именованный канал):FIFO используется, потому что процессы должны запускаться отдельно, чтобы получить и передать PID. ФИФО по- прежнему страдает от тех же проблем торчать для своевременной записи в дело
tail
получить в SIGPIPE , используйте--pid
опцию , так чтоtail
выходит , когда он замечает , чтоgrep
окончилось (обычно используется для контроля писателя процесса , а не читатель , ноtail
Безразлично» т действительно волнует). Опция-n 0
используется дляtail
того, чтобы старые строки не вызывали совпадения.Наконец, вы могли бы использовать хвост с сохранением состояния , это сохранит текущее смещение файла, поэтому последующие вызовы будут показывать только новые строки (это также обрабатывает вращение файла). В этом примере используется старый FWTK
retail
*:* Примечание, то же имя, программа отличается от предыдущей опции.
Вместо того, чтобы использовать цикл с переключением процессора, сравните временную метку файла с состоянием file (
.${LOGFILE}.off
) и sleep. Используйте «-T
», чтобы указать местоположение файла состояния, если требуется, выше предполагается, что текущий каталог. Не стесняйтесь пропустить это условие, или в Linux вы можете использовать более эффективноеinotifywait
:источник
retail
с тайм-аутом, например: «Если прошло 120 секунд, а розничная сеть все еще не прочитала строку, введите код ошибки и выйдите из магазина»?timeout
(coreutils) для запускаretail
и просто проверяет код завершения 124 по таймауту (timeout
уничтожит любую команду, которую вы используете, чтобы запустить после установленного времени)Это будет немного сложнее, так как вам придется войти в процесс управления и сигнализации. Больше kludgey было бы решением с двумя сценариями, использующим отслеживание PID. Лучше бы использовать именованные каналы, как это.
Какой сценарий оболочки вы используете?
Для быстрого и грязного, одно решение сценария - я сделал бы сценарий perl, используя File: Tail
Поэтому вместо того, чтобы печатать внутри цикла while, вы можете отфильтровать совпадения строк и выйти из цикла while, чтобы ваш сценарий продолжался.
Любой из них должен включать в себя лишь небольшое обучение для реализации контроля потока наблюдения, который вы ищете.
источник
maxinterval=>300
означает, что он будет проверять файл каждые пять минут. Поскольку я знаю, что моя строка появится в файле на мгновение, я использую гораздо более агрессивные опросы:maxinterval=>0.2, adjustafter=>10000
дождитесь появления файла
дождитесь появления строки в файле
https://superuser.com/a/743693/129669
источник
/path/to/the.file
размер 1,4 ГБ; тогда понятно, что это проблема. 2. После появления записи в журнале он дольше, чем необходимо, в худшем случае - 10 с.Я не могу представить себе более чистого решения, чем это:
хорошо, может быть имя может быть улучшено ...
Преимущества:
источник
Вам не нужен хвост, чтобы сделать это. Я думаю, что команда часов это то, что вы ищете. Команда watch контролирует вывод файла и может быть прервана с помощью опции -g при изменении вывода.
источник
Алекс, я думаю, что это поможет тебе.
эта команда никогда не даст запись в лог-файл, но будет молча grep ...
источник
logfile
иначе может пройти произвольно много времени, прежде чемtail
вывести другую строку и обнаружить, чтоgrep
она умерла (черезSIGPIPE
).Вот гораздо лучшее решение, которое не требует записи в файл журнала, что в некоторых случаях очень опасно или даже невозможно.
В настоящее время он имеет только один побочный эффект,
tail
процесс будет оставаться в фоновом режиме до следующей записи в журнал.источник
tail -n +0 -f
начинается с начала файла.tail -n 0 -f
начинается с конца файла.myscript.sh: line 14: 7845 Terminated sh -c 'tail...
tail
процесс продолжает работать в фоновом режиме.Другие решения здесь имеют несколько проблем:
Вот то, что я придумал, используя tomcat в качестве примера (удалите хэши, если вы хотите видеть журнал во время его запуска):
источник
Команда
tail
может быть фоновой, и ее pid отражается вgrep
подоболочке. Вgrep
подоболочке обработчик прерываний на EXIT может уничтожитьtail
команду.источник
Прочитайте их все. tldr: отделить окончание хвоста от grep.
Наиболее удобными являются две формы
и если у вас есть Баш
Но если этот хвост, сидящий на заднем плане, беспокоит вас, есть более хороший способ, чем пятерка или любой другой ответ здесь. Требуется Баш.
Или, если это не хвост, который выводит вещи,
источник
Попробуйте использовать inotify (inotifywait)
Вы настраиваете inotifywait для любого изменения файла, затем проверяете файл с помощью grep, если он не найден, просто перезапустите inotifywait, если он найден, выйдите из цикла ...
источник
$!
; inotifywait -e MODIFY / tmp / found; kill -KILL - $ MYPIDВы хотите уйти, как только строка написана, но вы также хотите уйти после тайм-аута:
источник
как насчет этого:
пока правда; делать, если [! -z $ (grep "myRegEx" myLog.log)]; затем сломаться; фи; сделанный
источник