Как «grep» непрерывный поток?

730

Возможно ли использовать grepнепрерывный поток?

Я имею в виду что-то вроде tail -f <file>команды, но с grepвыводом, чтобы сохранить только те строки, которые меня интересуют.

Я пытался, tail -f <file> | grep patternно кажется, что grepможет быть выполнено только после tailокончания, то есть никогда.

Матье Наполи
источник
9
Весьма вероятно, что программа, генерирующая файл, не сбрасывает вывод.
Стив
tail -f fileработает (я вижу новый вывод в режиме реального времени)
Матье Наполи
6
Было бы целесообразно unix.stackexchange.com
Люк М
@Luc действительно, не думал об этом
Матье Наполи
Может быть, нет новых строк в вашем потоке ввода? Если это так, grep не будет продолжаться.
Линч

Ответы:

1329

Включите grepрежим буферизации строки при использовании BSD grep (FreeBSD, Mac OS X и т. Д.)

tail -f file | grep --line-buffered my_pattern

Вам не нужно делать это для GNU grep (используется практически во всех Linux), поскольку он будет сбрасываться по умолчанию (YMMV для других Unix-подобных приложений, таких как SmartOS, AIX или QNX).

тад
источник
3
@MichaelNiemand и вы можете использовать tail -F file | grep - line-buffered my_pattern
jcfrei
47
@MichaelGoldshteyn Полегче. Люди одобряют это, потому что они находят эту страницу, когда они Google "буферизируют строку grep", и это решает проблему для них, которая может быть не совсем той, которая была задана в качестве вопроса.
дождь
4
Я пришел сюда, пытаясь получить вывод strace. Без --line-bufferedэтого не получится.
Sjas
5
@MichaelGoldshteyn (и авторы его комментариев): У меня всегда была эта проблема tail -f | grep, и я --line-bufferedее решаю (в Ubuntu 14.04, GNU grep version 2.16). Где реализована логика использования строки, если stdout является tty? В git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c , line_bufferedустанавливается только с помощью аргумента синтаксического анализа.
Aasmund Eldhuset
8
@MichaelGoldshteyn Я на MacOS с использованием BSD grep и без --line-bufferedя не получаю вывод. Однако после тестирования похоже, что GNU grep делает то, что вы описываете. Так что, как и большинство вещей Unix, это зависит от реализации вашей платформы. Поскольку в вопросе не указана платформа, ваша информация представляется ложной - после просмотра кода для BSD grep и сравнения его с GNU grep, поведение определенно контролируется опцией --line-buffered. Просто GNU grep сбрасывает по умолчанию.
Ричард Уэйт,
119

Я использую tail -f <file> | grep <pattern>все время.

Он будет ждать до сброса grep, а не до его завершения (я использую Ubuntu).

Ирит Катриэль
источник
4
Который может длиться довольно долго, поэтому постарайтесь не терять терпение.
glglgl
Сколько времени это может занять примерно?
Матье Наполи
@Matthieu: Зависит в основном от того, что вы ищете, и насколько велики буферы в вашей ОС. Если grep соответствует короткой строке каждые несколько часов, это будет за несколько дней до первого сброса.
tripleee
13
Tail не использует буферизацию вывода - grep использует.
XzKto
7
Нет, grep не выполняет буферизацию вывода, когда вывод идет на tty-устройство, как это ясно в этом ответе. Это делает буферизацию строки! Это правильный ответ и должен быть принятым ответом. Смотрите мой более длинный комментарий к принятому в настоящее время ( неправильному ) ответу для более подробной информации.
Михаил Гольдштейн
67

Я думаю, что ваша проблема в том, что grep использует некоторую буферизацию вывода. Пытаться

tail -f file | stdbuf -o0 grep my_pattern

он установит режим буферизации вывода grep в unbuffered.

XzKto
источник
7
И это имеет то преимущество, что кроме этого может использоваться для многих других команд grep.
Питер В. Мёрч
4
Однако, как я обнаружил после более подробного изучения, некоторые команды сбрасывают свои выходные данные только при подключении к tty, и для этого unbufferexpect-devпакете на debian) это king . Поэтому я бы использовал unbuffer поверх stdbuf.
Питер В. Мёрч,
5
@Peter V. Mørch Да, вы правы, иногда небуфер может работать там, где stdbuf не может. Но я думаю, что вы пытаетесь найти «волшебную» программу, которая всегда решит ваши проблемы вместо того, чтобы понять вашу проблему. Создание виртуального tty - это не связанная задача. Stdbuf делает именно то, что мы хотим (устанавливает стандартный выходной буфер, чтобы дать значение), в то время как unbuffer делает много скрытых вещей, которые мы можем не хотеть (сравнить интерактивныеtop с stdbuf и unbuffer) И на самом деле не существует «волшебного» решения: иногда небуферы тоже терпят неудачу, например, awk использует другую реализацию буфера (stdbuf тоже не будет работать).
XzKto
2
«Но я думаю, что вы пытаетесь найти« волшебную »программу, которая всегда решит ваши проблемы, а не поймет вашу проблему». - Я думаю ты прав! ;-)
Питер В. Мёрх
1
Еще немного информации о stdbufбуферизации `unbuffer и stdio 'на pixelbeat.org/programming/stdio_buffering
Тор Клингберг,
13

Если вы хотите найти совпадения во всем файле (а не только в хвосте) и хотите, чтобы он сидел и ждал новых совпадений, это прекрасно работает:

tail -c +0 -f <file> | grep --line-buffered <pattern>

-c +0Флаг говорит о том , что выход должен начинаться 0байт ( -c) с самого начала ( +) файла.

Кен Уильямс
источник
12

В большинстве случаев вы можете tail -f /var/log/some.log |grep fooи это будет работать просто отлично.

Если вам нужно использовать несколько greps в работающем файле журнала, и вы обнаружите, что ничего не выводите, вам может понадобиться вставить --line-bufferedкоммутатор в ваш средний grep, например, так:

tail -f /var/log/some.log | grep --line-buffered foo | grep bar
Дейл Андерсон
источник
7

вы можете рассматривать этот ответ как улучшение .. обычно я использую

tail -F <fileName> | grep --line-buffered  <pattern> -A 3 -B 5

-F лучше в случае поворота файла (-f не будет работать правильно, если файл повернут)

-A и -B полезны для получения линий непосредственно до и после появления шаблона. Эти блоки будут появляться между разделителями пунктирных линий

Но для меня я предпочитаю делать следующее

tail -F <file> | less

это очень полезно, если вы хотите искать внутри потоковых журналов. Я имею в виду, иди назад и вперед и посмотри

mebada
источник
4
grep -C 3 <pattern>, заменяет -A <N> и -B <N>, если N одинаково.
AKS
6

Не видел, чтобы кто-нибудь предложил мое обычное решение для этого:

less +F <file>
ctrl + c
/<search term>
<enter>
shift + f

Я предпочитаю это, потому что вы можете использовать ctrl + cдля остановки и навигации по файлу в любое время, а затем просто нажать, shift + fчтобы вернуться к живому потоковому поиску.

Hans.Loven.work
источник
4

Sed будет лучшим выбором ( редактор потоков )

tail -n0 -f <file> | sed -n '/search string/p'

и затем, если вы хотите, чтобы команда tail выходила, как только вы нашли конкретную строку:

tail --pid=$(($BASHPID+1)) -n0 -f <file> | sed -n '/search string/{p; q}'

Очевидно bashism: $ BASHPID будет идентификатором процесса команды tail. Команда sed будет следующей после tail в конвейере, поэтому идентификатор процесса sed будет $ BASHPID + 1.

Кристиан Херр
источник
1
Предположение о том, что следующий процесс, запущенный в system ( $BASHPID+1), будет вашим, во многих ситуациях ложно, и это ничего не делает для решения проблемы буферизации, о которой, вероятно, пытался спросить OP. В частности, рекомендуя sedболее grepздесь , кажется , как только вопрос (сомнительного) предпочтения. (Вы можете вести себя p;qс ним, grep -m 1если именно это вы пытаетесь донести.)
tripleee
Работает, команда sed печатает каждую строку, как только она готова, команда grep с этим --line-bufferedне имеет. Я искренне не понимаю минус 1.
MUY Бельгия
До сих пор было установлено, что буферизация является проблемой с grep . Никаких специальных действий для обработки буферизации строк с использованием sed не требуется , это поведение по умолчанию, поэтому я подчеркиваю поток слов . И правда, нет гарантии, что $ BASHPID + 1 будет правильным pid для последующего выполнения, но поскольку распределение pid является последовательным, а команда piped назначается pid сразу после него, это вполне вероятно.
Кристиан Герр
1

Да, это на самом деле будет работать просто отлично. Grepи большинство команд Unix работают с потоками по одной строке за раз. Каждая строка, которая выходит из хвоста, будет проанализирована и передана, если она совпадает.

Калеб
источник
2
Это не совсем правильно. Если grepэто последняя команда в цепочке каналов, она будет действовать так, как вы объясняете. Однако, если он находится посередине, он будет буферизовать около 8 Кбайт за раз.
Махмуд Аль-Кудси
1

Эта команда работает для меня (Suse):

mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN  >> logins_to_mail

сбор логинов к почтовому сервису

user10584393
источник
-1

у вас точно не получится

tail -f /var/log/foo.log |grep --line-buffered string2search

когда вы используете "colortail" в качестве псевдонима для tail, например. в баш

alias tail='colortail -n 30'

по псевдониму типа вы можете проверить, выводит ли он что-то вроде tail isan of alias colortail -n 30. тогда у тебя есть виновник :)

Решение:

удалить псевдоним с

unalias tail

убедитесь, что вы используете 'реальный' хвостовой двоичный файл с помощью этой команды

type tail

который должен вывести что-то вроде:

tail is /usr/bin/tail

и тогда вы можете запустить свою команду

tail -f foo.log |grep --line-buffered something

Удачи.

user882786
источник
-4

Используйте awk (еще одна отличная утилита bash) вместо grep, где у вас нет опции буферизации строки! Он будет непрерывно передавать ваши данные из хвоста.

вот как вы используете grep

tail -f <file> | grep pattern

Вот как бы вы использовали awk

tail -f <file> | awk '/pattern/{print $0}'
Atif
источник
6
Это не правильно; Awk "из коробки" выполняет буферизацию строки, как и большинство других стандартных инструментов Unix. (Более того, {print $0}это избыточно, поскольку печать выполняется по умолчанию при выполнении условия.)
tripleee