如何实现Python版的tail -F?

30 投票
10 回答
36900 浏览
提问于 2025-04-15 15:49

用Python怎么优雅地监视一个不断增长的文件,看看里面有没有出现某些关键词呢?

在命令行中,我可能会这样说:

tail -f "$file" | grep "$string" | while read hit; do
    #stuff
done

10 个回答

4

编辑:正如下面的评论所提到的,O_NONBLOCK 对于磁盘上的文件是无效的。如果有人想要从套接字、命名管道或其他进程中获取数据,这个信息还是有帮助的,但并没有真正回答最初的问题。原始回答仍然保留在下面,以供后人参考。(调用 tail 和 grep 是可行的,但从某种意义上来说,这也算不上一个真正的答案。)

你可以选择用 O_NONBLOCK 打开文件,然后使用 select 来检查是否可以读取数据,再用 read 来读取新数据,最后用字符串方法来过滤文件末尾的行……或者你也可以直接使用 subprocess 模块,让 tailgrep 像在命令行中那样为你处理这些工作。

7
def tail(f):
    f.seek(0, 2)

    while True:
        line = f.readline()

        if not line:
            time.sleep(0.1)
            continue

        yield line

def process_matches(matchtext):
    while True:
        line = (yield)  
        if matchtext in line:
            do_something_useful() # email alert, etc.


list_of_matches = ['ERROR', 'CRITICAL']
matches = [process_matches(string_match) for string_match in list_of_matches]    

for m in matches: # prime matches
    m.next()

while True:
    auditlog = tail( open(log_file_to_monitor) )
    for line in auditlog:
        for m in matches:
            m.send(line)

我用这个来监控日志文件。在完整的实现中,我把list_of_matches放在一个配置文件里,这样可以用于多个目的。在我计划的改进中,有一个是支持正则表达式,而不是简单的'in'匹配。

31

最简单的方法就是不断地从文件中读取内容,查看有什么新东西,然后测试一下有没有匹配的。

import time

def watch(fn, words):
    fp = open(fn, 'r')
    while True:
        new = fp.readline()
        # Once all lines are read this just returns ''
        # until the file changes and a new line appears

        if new:
            for word in words:
                if word in new:
                    yield (word, new)
        else:
            time.sleep(0.5)

fn = 'test.py'
words = ['word']
for hit_word, hit_sentence in watch(fn, words):
    print "Found %r in line: %r" % (hit_word, hit_sentence)

这个用 readline 的方法适用于你知道数据会按行出现的情况。

如果数据是某种流式的东西,你就需要一个缓冲区,这个缓冲区要比你要找的最大 word 还要大,先把它填满。这样就稍微复杂一些了……

撰写回答