Как прочитать одну строку из `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
немедленно выходит.