«Наблюдать» за выводом команды до тех пор, пока не будет обнаружена определенная строка, и затем выйти

29

Я ищу способ программно наблюдать за выводом команды, пока определенная строка не наблюдается, а затем выйти. Это очень похоже на этот вопрос, но вместо хвоста файла я хочу «хвостить» команду.

Что-то типа:

смотреть -n1 my_cmd | grep -m 1 "Строка, которую я ищу"

(Но это не работает для меня.)

ОБНОВЛЕНИЕ: Мне нужно уточнить, что my_cmd не выводит текст непрерывно, но его нужно повторно вызывать до тех пор, пока не будет найдена строка (вот почему я подумал о команде 'watch'). В этом отношении my_cmd похож на многие другие команды unix, такие как: ps, ls, lsof, last и т. Д.

gdw2
источник
Я бы подумал, что это возможно для tail -fвывода программы так же, как файл ... Я ошибаюсь?
Джоанис
@Joanis. Вы правы, но в моем случае «my_cmd» не производит непрерывный вывод и должен вызываться повторно (как и большинство команд: ps, ls, lsof и т. Д.)
gdw2

Ответы:

41

Используйте цикл:

until my_cmd | grep -m 1 "String Im Looking For"; do : ; done

Вместо этого :вы можете использовать sleep 1(или 0,2) для облегчения работы процессора.

Цикл выполняется до тех пор, пока grep не найдет строку в выводе команды. -m 1означает «достаточно одного совпадения», т.е. grep прекращает поиск после того, как находит первое совпадение.

Вы также можете использовать grep -qметод, который также завершает работу после нахождения первого совпадения, но без печати совпадающей строки.

choroba
источник
объяснение этой команды будет оценено.
Марк W
@MarkW: Обновлено.
Чороба
кто-то еще упомянул, grep -qчто является еще одним вариантом. grep выходит после нахождения строки.
Вс
обратите внимание, что эта команда будет многократно запускать указанную команду, что может быть или не быть желательным.
Адриен
1
@A__: желательно, как указано в ОП в разделе «Обновление».
Чороба
11
watch -e "! my_cmd | grep -m 1 \"String Im Looking For\""
  • ! отменяет код выхода командного конвейера
  • grep -m 1 выходит, когда строка найдена
  • watch -e возвращает, если произошла какая-либо ошибка

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

математический
источник
Спасибо за подробное объяснение, но оно не работает для меня. Моя watchкоманда (CentOS) не имеет -eфлага (который не должен иметь значения). Что еще более важно, однако, когда строка найдена, watch продолжает работать и не выходит. Кажется, что когда grep -mвыходит, он только убивает my_cmd, но не убивает watch.
gdw2
Нет, это имеет значение !, флаг "-e" предназначен для того, чтобы оставить наблюдение, когда команда имеет код ошибки, отличный от 0. Так как ее отсутствие, наблюдение собирается продолжаться на вашей платформе. Во всяком случае, приятно знать, что на моей установке Ubuntu 11.10 все в порядке. У меня также иногда возникают проблемы с Mac OSX из-за очень устаревших инструментов командной строки, и я до сих пор использую порты Mac для получения более современного программного обеспечения.
математика
Это останавливается, если шаблон найден, но не показывает никакого вывода, пока это не произойдет
Марк
Вы можете использовать teeдля этого, но это вводит в заблуждение новую строку, я не знаю, как обойти прямо сейчас:watch -n1 -e "! date | tee /dev/tty | grep --color -m 1 \"17\""
математика
Да, это не сработало для меня. watchпокорно перестает смотреть, когда строка найдена, но на самом деле она не завершается, пока вы не нажмете клавишу. Так близко.
mlissner
8

Для тех, у кого есть программа, которая постоянно записывает в stdout, все, что вам нужно сделать, это передать ее в grep с опцией 'single match'. Как только grep найдет соответствующую строку, он выйдет, что закроет стандартный вывод процесса, который передается в grep. Это событие должно естественным образом вызывать корректное завершение программы до тех пор, пока процесс не запишется снова .

Что произойдет, так это то, что процесс получит SIGPIPE, когда попытается записать в закрытый стандартный вывод после выхода grep. Вот пример с ping, который в противном случае работал бы бесконечно:

$ ping superuser.com | grep -m 1 "icmp_seq"

Эта команда будет соответствовать первому успешному pong, а затем завершится при следующей pingпопытке записи в stdout.


Тем не мение,

Не всегда гарантируется, что процесс снова запишет в stdout и, следовательно, может не вызывать SIGPIPE для поднятия (например, это может произойти при подключении файла журнала). Лучшее решение, которое мне удалось найти для этого сценария, включает запись в файл; пожалуйста, прокомментируйте, если вы думаете, что можете улучшить:

$ { tail -f log_file & echo $! > pid; } | { grep -m1 "find_me" && kill -9 $(cat pid) && rm pid; }

Разбивая это:

  1. tail -f log_file & echo $! > pid- привязывает файл, присоединяет процесс к фону и сохраняет PID ( $!) в файл. Вместо этого я попытался экспортировать PID в переменную, но, похоже, между этим моментом и тем, когда PID снова используется, возникает условие гонки.
  2. { ... ;}- сгруппировать эти команды вместе, чтобы мы могли направить вывод в grep, сохраняя текущий контекст (помогает при сохранении и повторном использовании переменных, но не смог заставить эту часть работать)
  3. | - труба с левой стороны к правой стороне
  4. grep -m1 "find_me" - найти целевую строку
  5. && kill -9 $(cat pid)- принудительно уничтожить (SIGKILL) tailпроцесс после grep выхода, когда он найдет соответствующую строку
  6. && rm pid - удалите файл, который мы создали
Блейк Регалия
источник
0
my_cmd | tail +1f | sed '/String Im Looking For/q'

Если tailне поддерживает +1fсинтаксис, попробуйте tail -f -n +1. (Он -n +1говорит, что должен начинаться с начала; tail -fпо умолчанию начинается с последних 10 строк вывода.)

Кит Томпсон
источник
Пожалуйста, смотрите мое обновление на вопрос.
gdw2
0

Добавить результат ваших программных вызовов в файл. Тогда tail -fэтот файл. Таким образом, это должно работать ... Я надеюсь.

Когда вы возобновите вызов этой программы, вам придется стереть файл или добавить к нему немного тарабарщины, чтобы он не совпадал с тем, что вы искали.

Joanis
источник