从经常更新的文件中读取

时间:2011-03-24 13:24:22

标签: python file-io fopen

我目前正在Linux系统上用python编写程序。目标是在找到特定字符串时读取日志文件并执行bash命令。日志文件由另一个程序不断写入。我的问题是:

如果我使用open()方法打开文件,我的Python文件对象会在实际文件被其他程序写入时更新,还是我必须按时间间隔重新打开文件?

由于

吉姆

更新: 到目前为止,感谢您的回答我或许应该提到该文件是由Java EE应用程序编写的,因此我无法控制何时将数据写入其中。我目前有一个程序每隔10秒重新打开一次该文件,并尝试从它上次读取的文件中的字节位置读取。目前它只打印出返回的字符串。我希望文件不需要重新打开,但是read命令可以以某种方式访问​​Java应用程序写入文件的数据。

#!/usr/bin/python
import time

fileBytePos = 0
while True:
    inFile = open('./server.log','r')
    inFile.seek(fileBytePos)
    data = inFile.read()
    print data
    fileBytePos = inFile.tell()
    print fileBytePos
    inFile.close()
    time.sleep(10)

感谢有关pyinotify和发电机的提示。我打算看看这些更好的解决方案。

8 个答案:

答案 0 :(得分:77)

我建议查看David Beazley的Generator Tricks for Python,尤其是第5部分:处理无限数据。它将实时处理Python等效的tail -f logfile命令。

# follow.py
#
# Follow a file like tail -f.

import time
def follow(thefile):
    thefile.seek(0,2)
    while True:
        line = thefile.readline()
        if not line:
            time.sleep(0.1)
            continue
        yield line

if __name__ == '__main__':
    logfile = open("run/foo/access-log","r")
    loglines = follow(logfile)
    for line in loglines:
        print line,

答案 1 :(得分:16)

“互动会话价值1000字”

>>> f1 = open("bla.txt", "wt")
>>> f2 = open("bla.txt", "rt")
>>> f1.write("bleh")
>>> f2.read()
''
>>> f1.flush()
>>> f2.read()
'bleh'
>>> f1.write("blargh")
>>> f1.flush()
>>> f2.read()
'blargh'

换句话说 - 是的,单个“开放”就可以了。

答案 2 :(得分:4)

以下是Jeff Bauer答案的略微修改版本,可以抵制文件截断。如果您的文件由logrotate处理,则非常有用。

import os
import time

def follow(name):
    current = open(name, "r")
    curino = os.fstat(current.fileno()).st_ino
    while True:
        while True:
            line = current.readline()
            if not line:
                break
            yield line

        try:
            if os.stat(name).st_ino != curino:
                new = open(name, "r")
                current.close()
                current = new
                curino = os.fstat(current.fileno()).st_ino
                continue
        except IOError:
            pass
        time.sleep(1)


if __name__ == '__main__':
    fname = "test.log"
    for l in follow(fname):
        print "LINE: {}".format(l)

答案 3 :(得分:3)

由于您的目标是Linux系统,因此您可以使用pyinotify在文件更改时通知您。

还有this技巧,可能对你有用。它使用file.seek来执行tail -f所做的事情。

答案 4 :(得分:1)

我不是这里的专家,但我认为您将不得不使用某种观察者模式来被动地观察文件,然后在发生更改时触发重新打开文件的事件。至于如何实际实现这一点,我不知道。

我认为open()不会像你建议的那样实时打开文件。

答案 5 :(得分:1)

如果你有代码读取文件在while循环中运行:

f = open('/tmp/workfile', 'r')
while(1):
    line = f.readline()
    if line.find("ONE") != -1:
        print "Got it"

并且您正在从另一个程序写入该文件(在追加模式下)。只要在文件中附加“ONE”,您就会得到打印件。你可以采取任何你想采取的行动。简而言之,您不必定期重新打开文件。

>>> f = open('/tmp/workfile', 'a')
>>> f.write("One\n")
>>> f.close()
>>> f = open('/tmp/workfile', 'a')
>>> f.write("ONE\n")
>>> f.close()

答案 6 :(得分:1)

我有一个类似的用例,我为它编写了以下代码片段。 虽然有些人可能会争辩说,这不是最理想的方式,但这可以完成工作并且看起来很容易理解。

def reading_log_files(filename):
    with open(filename, "r") as f:
        data = f.read().splitlines()
    return data


def log_generator(filename, period=1):
    data = reading_log_files(filename)
    while True:
        time.sleep(period)
        new_data = reading_log_files(filename)
        yield new_data[len(data):]
        data = new_data


if __name__ == '__main__':
    x = log_generator(</path/to/log/file.log>)
    for lines in x:
        print(lines)
        # lines will be a list of new lines added at the end

希望你觉得这有用

答案 7 :(得分:0)

这取决于您究竟想对文件做什么。这有两个潜在的用例:

  1. 从不断更新的文件(例如日志文件)中读取附加内容。
  2. 从不断覆盖的文件中读取内容(例如*nix系统中的网络统计文件)

由于其他人已经详细回答了如何解决场景 #1,我想帮助那些需要场景 #2 的人。基本上,您需要在调用 seek(0) n+1th 时间之前使用 read()(或您想要读取的任何位置)将文件指针重置为 0。

您的代码可能看起来有点像下面的函数。

def generate_network_statistics(iface='wlan0'):
    with open('/sys/class/net/' + iface + '/statistics/' + 'rx' + '_bytes', 'r') as rx:
        with open('/sys/class/net/' + iface + '/statistics/' + 'tx' + '_bytes', 'r') as tx:
            with open('/proc/uptime', 'r') as uptime:
                while True:
                    receive = int(rx.read())
                    rx.seek(0)
                    transmit = int(tx.read())
                    tx.seek(0)
                    uptime_seconds = int(uptime.read())
                    uptime.seek(0)
                    print("Receive: %i, Transmit: %i" % (receive, transmit))
                    time.sleep(1)