如何在python扩展上隐藏stdout / stderr

时间:2015-08-21 11:12:00

标签: python nagios net-snmp

我想在python脚本中隐藏stdout / stderr 但是当python模块需要打印时,打印不会进入python脚本本身,而是放在底层的.c扩展名中。

netsnmp python模块的情况,它需要编译,问题是有一些'printf'直接进入我想捕获的C(client_intf.c)代码中隐藏:

from netsnmp import *
import sys,os
devnull = open(os.devnull, 'w')
sys.stdout, sys.stderr = devnull, devnull
session = Session(DestHost='myhostthatdoesnotexisttogenerateerror',Version=2,Community='demopublic')

当我运行它时,我仍然有一些痕迹:

python test.py
getaddrinfo: myhostthatdoesnotexisttogenerateerror nodename nor servname provided, or not known
error:snmp_new_session: Couldn't open SNMP session

如何在stdout / stderr上删除这些消息?这很重要,因为我正在开发Nagios插件,我可以负担得起这样的输出(尽管如此,我想捕获它们以引发自定义异常)

3 个答案:

答案 0 :(得分:1)

Eric 的回答非常好,但它有一个重要的缺点:如果您已经使用 sys.stderr 来初始化某些东西,它将变得毫无用处。例如:如果您使用 sys.stderr 创建自定义错误记录器,它将停止工作。

这是修改后的版本:

import os
from contextlib import contextmanager

@contextmanager
def hide_stderr():
    """ Low level stderr supression. """
    newstderr = os.dup(2)
    devnull = os.open('/dev/null', os.O_WRONLY)
    os.dup2(devnull, 2)
    os.close(devnull)
    yield
    os.dup2(newstderr, 2)


@contextmanager
def hide_stdout():
    """ Low level stdout supression. """
    newstderr = os.dup(1)
    devnull = os.open('/dev/null', os.O_WRONLY)
    os.dup2(devnull, 1)
    os.close(devnull)
    yield
    os.dup2(newstderr, 1)


import sys

my_stderr = sys.stderr
print("Test 1: no stderr, no stdout")
with hide_stderr():
    with hide_stdout():
        print("1 stdout")
        print("2 stderr", file=my_stderr)
print("Test 2: no stderr, but stdout")
with hide_stderr():
    print("1 stdout")
    print("2 stderr", file=my_stderr)
print("Test 3: stderr, no stdout")
with hide_stdout():
    print("1 stdout")
    print("2 stderr", file=my_stderr)
print("Test 4: stderr and stdout")
print("1 stdout")
print("2 stderr", file=my_stderr)

请注意,主要区别在于末尾使用了 os.dup2。我们从我们所做的副本中恢复了文件描述符,我们不会弄乱 sys.stderr

答案 1 :(得分:0)

根据您的操作系统(似乎至少在Windows和Linux上都有效),以下内容将通过替换(低级别)文件描述符完全关闭fx stdout

nulf = os.open(os.devnull, os.O_WRONLY)
os.dup2(nulf, stdout.fileno())
os.close(nulf)

stderr相同似乎在交互模式下效果不佳(我猜python解释器试图将它的内容写入stderr)。

此解决方案具有与启动解释器时重定向到/dev/null或等效的解决方案相同的效果(除非它稍后生效),包括以本机代码生成的任何打印输出(当然,任何打印输出的脚本都是试图做)。如果您要进行任何日志记录,则必须为此打开文件(如果您想将os.devnull重定向到文件,也可以首先打开stdout以外的文件)。

请注意,通常程序不检查打印输出的结果,在这种情况下,可以简单地关闭描述符(即os.close(stdout.fileno())),但如果扩展实际检查结果并且基于不同的行为,则会失败在那。

答案 2 :(得分:0)

这是临时阻止stdout和stderr的代码:

from netsnmp import *
import sys,os

newstdout = os.dup(1)
newstderr = os.dup(2)
devnull = os.open('/dev/null', os.O_WRONLY)
os.dup2(devnull, 1)
os.dup2(devnull, 2)
os.close(devnull)
# stdout/stderr are blocked here
print "I will be back but nobody will know it"
session = Session(DestHost='myhostthatdoesnotexisttogenerateerror',Version=2,Community='demopublic')
sys.stdout = os.fdopen(newstdout, 'w')
sys.stderr = os.fdopen(newstderr, 'w')
# stdout/stderr are unblocked here
print "I'm back"