如何将写入文件的数据流式传输到标准输出

How to stream data written to file to stdout

我用 C 实现了共享库,它提供了一个函数 F。每次我调用 F 时,它都会将其输出记录在文件 error.log.

现在我正在尝试从 python 脚本中捕获 F 生成的输出(我正在使用 python 2.7.5,由于某些原因我无法更改我无法控制)。

我想将写入 error.log 的数据流式传输到不同的文件或 stdout。我不能只打开文件并解析它,因为它登录了更多内容,包括 F 之前和之后运行的输出。我只对我无法从日志中识别的特定执行感兴趣。这就是我尝试捕获输出的原因。

我尝试从 python 打开 error.log 然后更改文件描述符使其指向 stdout,但这似乎不起作用(我尝试了相同的stdoutstderr 确实有效)。

我做的大致是

with open('error.log') as logfile:
    with redirect_output(logfile, sys.stdout):
        function_implemented_in_C()

其中 redirect_output 是我实现的用于重定向的上下文管理器:

@contextmanager
def redirect_output(orig, dest):
    orig_fd = orig.fileno()
    with os.fdopen(os.dup(orig_fd)) as old_orig_fd:
        os.dup2(dest.fileno(), orig_fd)
    
    try:
        yield orig
    finally:
        # clean and restore fd's

我无法让它工作。知道我做错了什么吗?

更新:

我将问题简化为一个简单的脚本,但它似乎仍然不起作用。我想这与从共享库 (?) 中的函数生成的数据有关,因为如果我这样做但重定向 write 从从 python 打开的文件的调用,它就可以工作。这个例子工作正常:

import sys
import os

def foo():
    f = open('dummy.txt', 'wb', buffering=0)
    os.dup2(sys.stdout.fileno(), f.fileno())
    f.write('some test data\n')
    f.close()

if __name__ == '__main__':
    foo()

但这不是

import sys
import os

def foo():
    f = open('error.log', 'wb', buffering=0)
    os.dup2(sys.stdout.fileno(), f.fileno())
    function_implemented_in_C()
    f.close()

if __name__ == '__main__':
    foo()

我正在回答我自己的问题,以防有人遇到同样的问题。

这里的问题是在 error.log 中产生输出的 C 函数启动了一个不同的进程来执行任务。这使得无法轻松地将对文件的写入重定向到 stdout,因为文件描述符是特定于进程的。

因此,如果您的函数恰好使用相同的过程生成输出,那么以下应该有效

import sys
import os

def foo():
    f = open('dummy.txt', 'wb', buffering=0)
    os.dup2(sys.stdout.fileno(), f.fileno())
    f.write('some test data\n')
    f.close()

if __name__ == '__main__':
    foo()

如果不是这种情况,那么您可以通过设置从文件读取的循环来解决问题,以便它获取新写入的数据。类似的东西(我没有尝试过这段代码所以它不能 运行):

from multiprocessing import Process, Pipe

def foo(filename, pipe_conn):
    with open(filename) as f:
        while True:
            line = f.readline()
            do_something(line)
            if pipe_conn.poll(0.01):
                break

def bar(pipe_conn):
    function_implemented_in_C()
    pipe_conn.send(['done'])
    pipe_conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=bar, args=(child_conn,))
    p.start()
    foo(parent_conn)
    p.join()