【问题标题】:How to implement a pythonic equivalent of tail -F?如何实现与tail -F等效的pythonic?
【发布时间】:2010-12-14 18:56:12
【问题描述】:

查看增长文件尾部是否出现某些关键字的pythonic方法是什么?

我可能会说:

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

【问题讨论】:

标签: python file tail


【解决方案1】:

嗯,最简单的方法是不断地从文件中读取,检查新内容并测试命中。

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,然后先填充它。这样就有点复杂了……

【讨论】:

  • 反复打开文件,我有点草率了。您可以使用 os.stat 告诉您文件是否被修改。
  • 再看; open 只被调用一次。 readline 被反复调用。
【解决方案2】:
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”匹配。

【讨论】:

    【解决方案3】:

    您可以使用 select 来轮询文件中的新内容。

    def tail(filename, bufsize = 1024):
        fds = [ os.open(filename, os.O_RDONLY) ]
        while True:
            reads, _, _ = select.select(fds, [], [])
            if 0 < len(reads):
                yield os.read(reads[0], bufsize)
    

    【讨论】:

    • 您不能使用 select 来轮询文件以获取新数据 - 只有套接字。请参阅:docs.python.org/library/select.html,第一段最后一句:它不能用于常规文件以确定文件自上次读取以来是否已增长。
    【解决方案4】:

    编辑:正如下面的评论所指出的,O_NONBLOCK 不适用于磁盘上的文件。如果其他人一起寻找来自套接字或命名管道或其他进程的尾部数据,这仍然会有所帮助,但它没有回答所提出的实际问题。原始答案保留在下面以供后代使用。 (调用 tail 和 grep 可以,但无论如何都不是答案。)

    要么使用O_NONBLOCK 打开文件,然后使用select 轮询读取可用性,然后使用read 读取新数据,使用字符串方法过滤文件末尾的行...或者只使用subprocess 模块并让 tailgrep 为您完成工作,就像在 shell 中一样。

    【讨论】:

    • 大多数(如果不是全部)操作系统上的文件不支持非阻塞 IO。这包括 Linux 和 FreeBSD。如果是这样,非阻塞 IO 和 poll/select/whatever 的组合将与阻塞读取相同,这不会满足 OP 的需要。
    • @WGH:好点子!我想我是在写完这篇文章后才知道的,但是对于以后遇到这个问题的人来说,重要的是要注意,所以我在回答前加上了免责声明。
    【解决方案5】:

    你可以使用pytailf:简单的python tail -f wrapper

    from tailf import tailf    
    
    for line in tailf("myfile.log"):
        print line
    

    【讨论】:

    • 不要在tailf上浪费时间,它在内部调用“/usr/bin/tail”,这肯定不是你想要的。
    • "# Mwa-ha-ha, this is easiest way. Hardly portable to windowz, but who cares? TAILF_COMMAND = ['/usr/bin/tail', '-F', '-n']"
    【解决方案6】:

    看起来有一个包:https://github.com/kasun/python-tail

    【讨论】:

      【解决方案7】:

      如果您不能将问题限制为基于行的读取,则需要使用块。

      这应该可行:

      import sys
      
      needle = "needle"
      
      blocks = []
      
      inf = sys.stdin
      
      if len(sys.argv) == 2:
          inf = open(sys.argv[1])
      
      while True:
          block = inf.read()
          blocks.append(block)
          if len(blocks) >= 2:
              data = "".join((blocks[-2], blocks[-1]))
          else:
              data = blocks[-1]
      
          # attention, this needs to be changed if you are interested
          # in *all* matches separately, not if there was any match ata all
          if needle in data:
              print "found"
              blocks = []
          blocks[:-2] = []
      
          if block == "":
              break
      

      挑战在于确保匹配针,即使它被两个块边界分开。

      【讨论】:

      • 好收获;我整天都在日志文件中游泳,有时我忘记了并非所有数据都排成一行。
      【解决方案8】:

      据我所知,Python 函数列表中没有“tail”等价物。解决方案是使用 tell()(获取文件大小)和 read() 来计算结尾行。

      这篇博文(不是我写的)有写出来的功能,看起来很适合我! http://www.manugarg.com/2007/04/real-tailing-in-python.html

      【讨论】:

        【解决方案9】:

        如果您只需要一个非常简单的 Python 3 解决方案来处理编写的文本文件的行,并且您不需要 Windows 支持,那么这对我来说效果很好:

        import subprocess
        def tailf(filename):
            #returns lines from a file, starting from the beginning
            command = "tail -n +1 -F " + filename
            p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, universal_newlines=True)
            for line in p.stdout:
                yield line
        for line in tailf("logfile"):
            #do stuff
        

        它会阻塞等待写入新行,因此不适合异步使用而无需进行一些修改。

        【讨论】:

        • 如果 Git 包位于 %path% 中,它也可以在 Windows 上运行
        【解决方案10】:

        你可以使用collections.deque来实现tail。

        来自http://docs.python.org/library/collections.html#deque-recipes ...

        def tail(filename, n=10):
            'Return the last n lines of a file'
            return deque(open(filename), n)
        

        当然,这会读取整个文件内容,但它是一种简洁明了的tail实现方式。

        【讨论】:

        • 这回答了一个不同的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 2012-04-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多