我想通过以下方式跟踪文件的更改tail -f
,然后在终止之前打印出与grep
命令匹配的第一行。最简单的方法是什么?到目前为止,我一直在尝试以下方法:
tail -f foo.log | grep 'bar' | head -1
但是,管道只是挂起,大概是由于缓冲。我也试过了
tail -f foo.log | grep --line-buffered 'bar' | head -1
这会打印出该行,但除非我按下 ^C,否则进程不会终止,大概是因为需要第二行输入才能终止head -1
。解决这个问题的最佳方法是什么?
答案1
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 时 tail 可能已经消失了。
答案2
<() 结构仅在 bash 中有效。我的解决方案是:
sleep $timeout &
pid=$!
tail -n +0 --pid=$pid -F "$file" | ( grep -q -m 1 "$regexp" && kill -PIPE $pid )
wait $pid
除了在 POSIX shell 中工作之外,另一个优点是可以使用超时值来限制等待时间。请注意,此脚本的结果代码是反转的:0 表示已达到超时。
答案3
tail -f
等待SIGPIPE
,正如 depesz 指出的那样,只能tail -f
在成功匹配后通过向关闭的管道再次写入 来触发,如果命令进入后台并且其后台 pid 被发送到子shell,grep -m 1
则可以避免这种情况,而子shell反过来会实现在退出时明确终止。tail -f
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)
答案4
我认为申请解除缓冲像这样,
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
立即退出。