2012-06-21 38 views
18

我有一個Python腳本正在使用我的僱主提供的一些封閉的Python函數(即我無法編輯這些函數)。當我調用這些函數時,它們將打印輸出到我想禁止的linux終端。我試過重定向stdout/stderr;從Python函數中抑制stdout/stderr打印

orig_out = sys.stdout 
sys.stdout = StringIO() 
rogue_function() 
sys.stdout = orig_out 

但這無法捕捉輸出。我認爲我通過Python調用的函數(來自上面的rogue_function())真的是編譯C代碼的包裝器,它們實際上正在進行打印。

有沒有人知道我可以通過函數(以及函數調用的任何子函數)對stdout/stderr進行任何打印的「深度捕獲」?

UPDATE

我最終採取如下選擇的答案中概述的方法和寫作上下文管理器來剿輸出和錯誤:

# Define a context manager to suppress stdout and stderr. 
class suppress_stdout_stderr(object): 
    ''' 
    A context manager for doing a "deep suppression" of stdout and stderr in 
    Python, i.e. will suppress all print, even if the print originates in a 
    compiled C/Fortran sub-function. 
     This will not suppress raised exceptions, since exceptions are printed 
    to stderr just before a script exits, and after the context manager has 
    exited (at least, I think that is why it lets exceptions through).  

    ''' 
    def __init__(self): 
     # Open a pair of null files 
     self.null_fds = [os.open(os.devnull,os.O_RDWR) for x in range(2)] 
     # Save the actual stdout (1) and stderr (2) file descriptors. 
     self.save_fds = [os.dup(1), os.dup(2)] 

    def __enter__(self): 
     # Assign the null pointers to stdout and stderr. 
     os.dup2(self.null_fds[0],1) 
     os.dup2(self.null_fds[1],2) 

    def __exit__(self, *_): 
     # Re-assign the real stdout/stderr back to (1) and (2) 
     os.dup2(self.save_fds[0],1) 
     os.dup2(self.save_fds[1],2) 
     # Close all file descriptors 
     for fd in self.null_fds + self.save_fds: 
      os.close(fd) 

要使用這個你剛纔:

with suppress_stdout_stderr(): 
    rogue_function() 

這個作品「相當不錯」。它確實抑制了混淆我的腳本的流氓功能的打印輸出。我在測試中注意到它允許通過引發異常以及某些記錄器打印,並且我不完全清楚爲什麼。我認爲這與有關,當這些消息被髮送到stdout/stderr(我認爲它發生在我的上下文管理器退出後)。如果任何人都可以證實這一點,我會很樂意聽取細節...

+1

不[這種方法](http://stackoverflow.com/a/978264/344821)(從相關的側邊欄)工作? – Dougal

+0

而不是設置'sys.stdout'到'StringIO的()',你嘗試將其設置到一個文件?即'sys.stdout的=打開( 'log.txt的', 'W')' – carmenism

+0

杜格爾,謝謝,看起來很有希望,我會嘗試一下明天。我嘗試將它指向一個自定義的NullPointer()類,並且這也不起作用。 – jeremiahbuddha

回答

5

This approach(通過相關側欄找到)可能工作。它重新分配文件描述符,而不僅僅是sys.stdout中的包裝文件等。

-2

如果您運行的是基於Linux的機器上運行此腳本,你應該能夠:

$> ./runscript.py > output.txt 
+0

我不想壓制腳本產生的所有輸出,只會產生這些特定函數產生的雜散輸出。否則,是的,這是最簡單的解決方案... – jeremiahbuddha

+0

這會幫助:之前和那些特定功能後添加的打印。解析與正則表達式的輸出得到上述 – GeneralBecos

1

你有沒有嘗試重定向stderr呢? 例如

sys.stdout = StringIO(); 
sys.stderr = StringIO(); 
foo(bar); 
sys.stdout = sys.__stdout__; # These are provided by python 
sys.stderr = sys.__stderr__; 

另外使用StringIO可能會使用額外的內存。您可以使用虛擬設備(例如http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html)。

+0

添加的打印之間擺脫一切的呀,我都嘗試輸出和錯誤,沒有骰子... – jeremiahbuddha

+0

它可能是C代碼被直接寫入標準輸出;你可能無法重定向。抱歉。 – Bob

1

我的解決方案與您的解決方案類似,但使用的是contextlib,它稍短且易於理解(恕我直言)。

import contextlib 


@contextlib.contextmanager 
def stdchannel_redirected(stdchannel, dest_filename): 
    """ 
    A context manager to temporarily redirect stdout or stderr 

    e.g.: 


    with stdchannel_redirected(sys.stderr, os.devnull): 
     if compiler.has_function('clock_gettime', libraries=['rt']): 
      libraries.append('rt') 
    """ 

    try: 
     oldstdchannel = os.dup(stdchannel.fileno()) 
     dest_file = open(dest_filename, 'w') 
     os.dup2(dest_file.fileno(), stdchannel.fileno()) 

     yield 
    finally: 
     if oldstdchannel is not None: 
      os.dup2(oldstdchannel, stdchannel.fileno()) 
     if dest_file is not None: 
      dest_file.close() 

爲什麼我創建這個的上下文是在this blog post。與你的想法類似。

我在setup.py使用這樣的:

with stdchannel_redirected(sys.stderr, os.devnull): 
    if compiler.has_function('clock_gettime', libraries=['rt']): 
     libraries.append('rt') 
1

沒有真正的OP請求,但我需要隱藏和存儲輸出,並沒有像如下:

from io import StringIO 
import sys 

class Hider: 
    def __init__(self, channels=('stdout',)): 
     self._stomach = StringIO() 
     self._orig = {ch : None for ch in channels} 

    def __enter__(self): 
     for ch in self._orig: 
      self._orig[ch] = getattr(sys, ch) 
      setattr(sys, ch, self) 
     return self 

    def write(self, string): 
     self._stomach.write(string) 

    def flush(self): 
     pass 

    def autopsy(self): 
     return self._stomach.getvalue() 

    def __exit__(self, *args): 
     for ch in self._orig: 
      setattr(sys, ch, self._orig[ch]) 

用法:

with Hider() as h: 
    spammy_function() 
    result = h.autopsy() 

(僅用Pytho測試N 3)

編輯:現在允許選擇stderrstdout或兩者,如在Hider([stdout, stderr])

+0

這應該是Python 3的公認答案!你也可以添加seld._stderr == sys.stderr; sys.stderr =自<...> – neok

+0

@neok:廣義一點,感謝您的建議 –