Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何防止在python的标准输出上打印C共享库?_Python_Ctypes_Python 2.x - Fatal编程技术网

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

如何防止在python的标准输出上打印C共享库?,python,ctypes,python-2.x,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 i

我使用一个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)
第一种方法有一半的效果:
-出于某种原因,在移动stdout之前,它需要一个“print”语句,否则helloword总是被打印出来的。因此,它将打印一个空行,而不是库通常输出的所有模糊。
-更烦人的是,重定向到文件时失败:

$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;
}
这会打印“hello”,即使我在printf之后才看到libc.fflush(stdout)。我开始认为用python做我想做的事情是不可能的。或者,我获取标准输出文件指针的方式可能不正确


你觉得怎么样?

你不能像在Python中那样做吗?导入sys并将sys.stdout和sys.stderr指向不是默认sys.stdout和sys.stderr的对象?我总是在一些应用程序中这样做,在这些应用程序中,我必须从库中提取输出。

以下是我最终的做法。我希望这对其他人有用(这在我的linux工作站上很有用)

我很自豪地介绍libshutup,它是为关闭外部库而设计的

1) 复制以下文件

// file: shutup.c
#include <stdio.h>
#include <unistd.h>

static char buf[20];
static int saved_stdout;

void stdout_off() {
    saved_stdout = dup(1);
    freopen("/dev/null", "w", stdout);
}

void stdout_on() {
    sprintf(buf, "/dev/fd/%d", saved_stdout);
    freopen(buf, "w", stdout);
}
3) 在这样的代码中使用它

from ctypes import *
shutup = CDLL("libshutup.so")

shutup.stdout_off()

# Let's pretend this printf comes from the external lib
libc = CDLL("libc.so.6")
libc.printf("hello\n")

shutup.stdout_on()

是的,你真的想使用
os.dup2
而不是
os.dup
,就像你的第二个想法一样。您的代码看起来有些迂回。不要乱动
/dev
条目,除了
/dev/null
,这是不必要的。这里也不需要用C写任何东西

诀窍是使用
dup
保存
stdout
fde,然后将其传递给
fdopen
以创建新的
sys.stdout
Python对象。同时,打开一个FDE到
/dev/null
并使用
dup2
覆盖现有的
stdout
FDE。然后将旧FDE关闭到
/dev/null
。调用
dup2
是必要的,因为我们无法告诉
open
我们希望它返回哪个FDE,
dup2
确实是唯一的方法

编辑:如果要重定向到文件,则stdout没有行缓冲,因此必须刷新它。您可以从Python中执行此操作,它将与C正确地进行互操作。当然,如果您在向标准输出写入任何内容之前调用此函数,那么这并不重要

下面是我刚刚测试的一个在我的系统上运行的示例

import zook
import os
import sys

def redirect_stdout():
    print "Redirecting stdout"
    sys.stdout.flush() # <--- important when redirecting to files
    newstdout = os.dup(1)
    devnull = os.open(os.devnull, os.O_WRONLY)
    os.dup2(devnull, 1)
    os.close(devnull)
    sys.stdout = os.fdopen(newstdout, 'w')

zook.myfunc()
redirect_stdout()
zook.myfunc()
print "But python can still print to stdout..."
和重定向到文件

$ python2.5 test.py > test.txt
$ cat test.txt
myfunc called
Redirecting stdout
But python can still print to stdout...
组合两个答案-&到上下文管理器,仅在其范围内阻止打印到stdout(第一个答案中的代码阻止了任何外部输出,后一个答案在末尾遗漏了sys.stdout.flush()):

基于。此变体尝试避免泄漏文件描述符:

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

从哪里获得SEGFULT(堆栈跟踪)?而且,真诚地。。。为共享库的开发者感到羞耻。从共享库中直接写入stdout而不提供改变这种行为的方法是baaad。不幸的是,我找不到任何从Python中重定向stdout的方法。我认为您在这里使用C包装您的共享库,用包装器制作dll并使用ctypes调用它,这是正确的。我相信你的segfault是由于
sprintf
造成的,但我真的不知道问题出在哪里。可能的重复我再也没有segfault了(我不再使用sprintf),很抱歉让你的评论过时了,但我觉得这篇文章没有堆栈跟踪已经足够长了。相关:不,因为
sys.std*
仅由Python代码使用,而C库直接使用FDs 0到2。这应该是一个注释,而不是一个答案。实际上,这是一个答案,但没有有效的代码,我可以看出它是如何无法传达消息的。回到2011年,我没有意识到你在StackOverflow上需要做多少牵手。不管怎样,我不知道你从挑刺和投否决票中得到了什么…2.5年后。所有这些都将取代5行Python。如果你有更好的答案,我很高兴拥有它。您可以运行上面的每个示例,看到它们都打印了一些内容。现在,这是唯一对我有效的解决方案。这个问题在这个问题上得到了解决,这个问题是重复的。@Ignacio不,不是。只要试着运行我在第二次尝试中给出的代码,这正是你在文章中的建议,你就会看到。很好的解决方案,它适用于我的具体情况。我以前找到的所有解决方案都没有完全奏效。我想做的一件事是将stdout作为字符数组返回,以便在Python中进行控制,而不是写入“/dev/fd/%d”。有办法吗?我对C&C两种类型都很陌生。如果这是一个太明显的问题,请原谅我的无知。感谢@YinonEhrlich建议使用
os.devnull
而不是
'/dev/null'
。该编辑被审阅者拒绝,我不同意拒绝。这种方法可以用于重定向到从记录器模块创建的文件吗?理论上是的,但您如何知道要替换哪个文件描述符?你怎么知道要冲洗哪个对象?把它想象成用炸药开门,因为你找不到钥匙。这是问题的关键。。如果使用Logger对象,以及如何使用Logger对象?我甚至不知道“Logger对象”是什么,这里可能会有一个更大的讨论(比如,你想做什么?旋转日志?重定向日志?)
#include <Python.h>
#include <stdio.h>

static PyObject *
myfunc(PyObject *self, PyObject *args)
{
    puts("myfunc called");
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef zookMethods[] = {
    {"myfunc",  myfunc, METH_VARARGS, "Print a string."},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initzook(void)
{
    (void)Py_InitModule("zook", zookMethods);
}
$ python2.5 test.py
myfunc called
Redirecting stdout
But python can still print to stdout...
$ python2.5 test.py > test.txt
$ cat test.txt
myfunc called
Redirecting stdout
But python can still print to stdout...
class HideOutput(object):
    '''
    A context manager that block stdout for its scope, usage:

    with HideOutput():
        os.system('ls -l')
    '''

    def __init__(self, *args, **kw):
        sys.stdout.flush()
        self._origstdout = sys.stdout
        self._oldstdout_fno = os.dup(sys.stdout.fileno())
        self._devnull = os.open(os.devnull, os.O_WRONLY)

    def __enter__(self):
        self._newstdout = os.dup(1)
        os.dup2(self._devnull, 1)
        os.close(self._devnull)
        sys.stdout = os.fdopen(self._newstdout, 'w')

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout = self._origstdout
        sys.stdout.flush()
        os.dup2(self._oldstdout_fno, 1)
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