Skip to main content
 首页 » 编程设计

python之如何防止 C 共享库在 python 中的标准输出上打印

2025年05月04日9wayfarer

我使用一个 python 库,该库导入一个在 stdout 上打印的 C 共享库。我想要一个干净的输出,以便将它与管道一起使用或在文件中重定向。打印是在 python 之外的共享库中完成的。

一开始,我的做法是:

# file: test.py 
import os 
from ctypes import * 
from tempfile import mktemp 
 
libc = CDLL("libc.so.6") 
 
print # That's here on purpose, otherwise hello word is always printed 
 
tempfile = open(mktemp(),'w') 
savestdout = os.dup(1) 
os.close(1) 
if os.dup(tempfile.fileno()) != 1: 
    assert False, "couldn't redirect stdout - dup() error" 
 
# let's pretend this is a call to my library 
libc.printf("hello world\n") 
 
os.close(1) 
os.dup(savestdout) 
os.close(savestdout) 

第一种方法只奏效了一半:
- 出于某种原因,在移动标准输出之前它需要一个“print”语句,否则总是打印 hello word。因此,它将打印一个空行,而不是库通常输出的所有模糊内容。
- 更烦人的是,重定向到文件时失败:

$python test.py > foo && cat foo 
 
hello world 

我的第二次 python 尝试的灵感来自评论中给出的另一个类似线程:

import os 
import sys 
from ctypes import * 
libc = CDLL("libc.so.6") 
 
devnull = open('/dev/null', 'w') 
oldstdout = os.dup(sys.stdout.fileno()) 
os.dup2(devnull.fileno(), 1) 
 
# We still pretend this is a call to my library 
libc.printf("hello\n") 
 
os.dup2(oldstdout, 1) 

这个也无法阻止打印“hello”。

因为我觉得这有点低级,所以我决定完全使用 ctypes。我从这个不打印任何内容的 C 程序中获得灵感:

#include <stdio.h> 
 
int main(int argc, const char *argv[]) { 
    char buf[20]; 
    int saved_stdout = dup(1); 
    freopen("/dev/null", "w", stdout); 
 
    printf("hello\n"); // not printed 
 
    sprintf(buf, "/dev/fd/%d", saved_stdout); 
    freopen(buf, "w", stdout); 
 
    return 0; 
} 

我构建了以下示例:

from ctypes import * 
libc = CDLL("libc.so.6") 
 
saved_stdout = libc.dup(1) 
stdout = libc.fdopen(1, "w") 
libc.freopen("/dev/null", "w", stdout); 
 
libc.printf("hello\n") 
 
libc.freopen("/dev/fd/" + str(saved_stdout), "w", stdout) 

这会打印“hello”,即使我在 printf 之后才使用 libc.fflush(stdout)。我开始认为可能无法在 python 中做我想做的事。或者我获取指向 stdout 的文件指针的方式不正确。

你怎么看?

请您参考如下方法:

基于 @Yinon Ehrlich's answer .此变体试图避免泄漏文件描述符:

import os 
import sys 
from contextlib import contextmanager 
 
@contextmanager 
def stdout_redirected(to=os.devnull): 
    ''' 
    import os 
 
    with stdout_redirected(to=filename): 
        print("from Python") 
        os.system("echo non-Python applications are also supported") 
    ''' 
    fd = sys.stdout.fileno() 
 
    ##### assert that Python and C stdio write using the same file descriptor 
    ####assert libc.fileno(ctypes.c_void_p.in_dll(libc, "stdout")) == fd == 1 
 
    def _redirect_stdout(to): 
        sys.stdout.close() # + implicit flush() 
        os.dup2(to.fileno(), fd) # fd writes to 'to' file 
        sys.stdout = os.fdopen(fd, 'w') # Python writes to fd 
 
    with os.fdopen(os.dup(fd), 'w') as old_stdout: 
        with open(to, 'w') as file: 
            _redirect_stdout(to=file) 
        try: 
            yield # allow code to be run with the redirected stdout 
        finally: 
            _redirect_stdout(to=old_stdout) # restore stdout. 
                                            # buffering and flags such as 
                                            # CLOEXEC may be different