如何防止C共享库在python中的stdout上打印?

发布于 2021-01-29 15:06:04

我使用的是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)

第一种方法是行之有效的:
-出于某种原因,它在移动标准输出之前需要一个“打印”语句,否则总是打印问候词。结果,它将打印一个空行,而不是库通常输出的所有模糊测试。
-更烦人的是,重定向到文件时失败:

$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)

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

你怎么看?

关注者
0
被浏览
66
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    基于@Yinon
    Ehrlich的答案
    。此变体尝试避免泄漏文件描述符:

    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
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看