在Python中读取正在写入的打开文件句柄

1 投票
2 回答
2730 浏览
提问于 2025-04-16 14:37

我知道这是流处理中的一个经典问题,但我不知道在Python中该怎么处理。我有一个文件句柄,它正在被一个活跃的进程写入。我想逐行读取这个文件句柄中的内容,但我不想因为等待读取而导致程序卡住。我会一直读取,直到文件结束(EOF)或者循环读取60秒,以先到者为准。如果有人能给我一些建议,我会非常感激。下面是我对这个问题的伪代码描述。

proc = genprocess("command")
found_a = False
found_b = False
start = time.time()
while True:
    line = proc.readline()
    while line:
        if not found_a and grep(pattern_a, line):
            found_a = True
            print "Found A, now looking for B"
        elif not found_b and grep(pattern_b, line):
            found_b = True
            print "Found B, all done"
            break
    if time.time() - start > 60:
        break
    else:
        time.sleep(5)

proc.kill()

问题在于,这段代码每次只读取一行。我希望循环内部能够尽可能多地迭代,但要因为等待新内容写入文件而卡住。一旦读取到的内容足够多,它应该暂停5秒,以便让更多内容积累。

2 个回答

2

如果你在Unix环境下工作,可以使用Python的select模块来等待文件句柄上的数据。此外,你还可以使用Python的fcntl模块来将文件句柄设置为非阻塞模式,具体可以参考这个问题

举个例子,假设你的proc变量是一个普通的文件句柄,并且支持fileno()

file_num = proc.fileno()
old_flags = fcntl.fcntl(file_num, fcntl.F_GETFL)
fcntl.fcntl(file_num, fcntl.F_SETFL, old_flags | os.O_NONBLOCK)
1

上面提到的关于fcntl的例子还不错(除了它让程序一直在忙着循环检查),不过我最后还是用了“select”来实现差不多想要的功能。

    started = False
    while True:
        if (time.time() - start > wait_for) or started:
            break
        (rlist, wlist, xlist) = select([proc.stdout], [], [], wait_interval)
        if len(rlist) > 0:
            line = rlist[0].readline() # read one line (this blocks until '\n' is read)
        else: # nothing available to read from proc.stdout
            print ".",
            sys.stdout.flush()
            time.sleep(1)
            continue
        if re.search("daemon started", line):
            started = True

    if not started:
        proc.kill() # don't leave the process running if it didn't start properly

如果用户可能会按CTRL-C来中断这个操作,那么把整个过程放在一个try/except块里,并且监测KeyboardInterrupt,这样就可以调用proc.kill()来结束进程,而不是让它在后台继续运行。

撰写回答