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

标签 python ctypes python-2.x

我使用一个 python 库,该库导入一个在标准输出上打印的 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)

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

$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”,即使我 libc.fflush(stdout) 就在 printf 之后。我开始认为在 python 中做我想做的事情可能是不可能的。或者也许我获取指向标准输出的文件指针的方式不正确。

你怎么看?

最佳答案

基于 @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

关于python - 如何防止 C 共享库在 python 的标准输出上打印?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5081657/

相关文章:

python - 操作格式良好的 xml(在 linux 下运行的任何语言)

python - Ipython笔记本水平缩放

python - 如何在 Python 中正确声明 ctype 结构 + 联合?

python - 如何将数组转换为字符,以便我可以从c中的函数返回该字符值。然后,在Python中调用它。

python - __new__ 是检索 SQLAlchemy 对象的好方法吗

python - 为什么嵌套循环的顺序之间存在性能差异?

python - Matplotlib:将网格线保留在图形后面,但 y 轴和 x 轴在上面

python - 将 ByteArray 从 Python 传递给 C 函数

python - 从列表指向字典变量

python - django-admin:添加额外的总计行