Как прочитать одну строку из `tail -f` через конвейер, а затем завершить?

Я хотел бы следить за изменениями в файле через tail -f а затем распечатайте первую строку, которая соответствует grep Команда до завершения. Какой самый простой способ сделать это? До сих пор я экспериментировал с такими вещами, как:

tail -f foo.log | grep 'bar' | head -1

Однако конвейер просто зависает, предположительно из-за буферизации. Я тоже пробовал

tail -f foo.log | grep --line-buffered 'bar' | head -1

Это выводит строку, но процесс не завершается, пока я не нажму ^C, предположительно, потому что вторая строка ввода необходима для завершения head -1, Какой лучший способ решить эту проблему?

5 ответов

Решение
tail -f foo.log | grep -m 1 bar

если файл foo.log записан с разногласиями, вы можете сделать:

grep -m 1 bar <( tail -f foo.log )

Следует отметить, что tail -f будет оставаться в фоновом режиме, пока не получит еще одну строку для вывода. Если это занимает много времени, это может быть проблематично. Решение в таком случае:

grep -m 1 bar <( exec tail -f foo.log ); kill $! 2> /dev/null

kill убьет оставшийся процесс tail -f, и мы скрываем ошибки, потому что, возможно, хвост исчезнет к тому времени, когда будет вызвано kill.

Конструкция <() работает только в bash. Мое решение:

sleep $timeout &
pid=$!
tail -n +0 --pid=$pid -F "$file" | ( grep -q -m 1 "$regexp" && kill -PIPE $pid )
wait $pid

Помимо работы в оболочке POSIX, еще одним преимуществом является то, что период ожидания может быть ограничен значением тайм-аута. Обратите внимание, что код результата этого скриптлета инвертирован: 0 означает, что истекло время ожидания.

tail -f в ожидании SIGPIPE, который, как уже указывал depesz, может быть вызван только другой записью tail -f к закрытой трубе после успешного grep -m 1 можно избежать, если tail -f команда становится фоновой, а ее фоновый pid отправляется на grep подоболочка, которая в свою очередь реализует trap на выходе, который убивает tail -f в явном виде.

#(tail -f foo.log & echo ${!} ) |
(tail -f foo.log & echo ${!} && wait ) | 
   (trap 'kill "$tailpid"; trap - EXIT' EXIT; tailpid="$(head -1)"; grep -m 1 bar)

Опираясь на ответ /questions/928302/kak-prochitat-odnu-stroku-iz-tail-f-cherez-konvejer-a-zatem-zavershit/928327#928327, я понял, что если tail начнется слишком поздно, возможно, какой-то экземпляр указанной строки уже прошел. Решение состоит в том, чтобы сделать хвостовой список всего файла.

grep -m 1 bar <( exec tail -n +1 -f foo.log ); kill $!

Я думаю, что применение unbuffer, как это,

tail -f foo.log | unbuffer -p grep 'bar' | head -1

должно сработать. Я не в системе, где я могу проверить это, и я не могу объяснить, почему это будет работать, пока grep --line-buffered не. Тем не менее, я попробовал эту альтернативу, которая, кажется, работает:

tail -f foo.log | sed -n '/bar/{p;q}'

Когда матч в bar найден, p печатает это и q немедленно выходит.

Другие вопросы по тегам