48

雇用主から提供されたクローズドボックスの Python 関数 (つまり、これらの関数を編集できない) を使用する Python スクリプトがあります。これらの関数を呼び出すと、抑制したい Linux 端末に出力が出力されます。stdout / stderr をリダイレクトしようとしました;

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

しかし、これは出力をキャッチできません。Python経由で呼び出している関数(上記の rogue_function() )は、実際に印刷を行っているコンパイル済みCコードの実際のラッパーだと思います。

関数 (および関数が呼び出すサブ関数) によって stdout / stderr に渡された任意の印刷の「ディープ キャプチャ」を実行できる方法を知っている人はいますか?

更新

以下の選択した回答で概説されている方法を採用し、stdoutとstderrを抑制するコンテキストマネージャーを作成しました。

# 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 に送信されるときに何か関係があると思います (コンテキストマネージャーが終了した後に発生すると思います)。誰かがこれを確認できるなら、私は詳細を聞くことに興味があります...

4

9 に答える 9

12

このアプローチ(関連するサイドバーから見つけられる) はうまくいくかもしれません。sys.stdout などでラッパーだけでなく、ファイル記述子を再割り当てします。

于 2012-06-21T22:00:12.967 に答える
3

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

于 2012-06-21T03:02:51.640 に答える
2

私のソリューションはあなたのものに似ていますcontextlibが、少し短くて理解しやすいものを使用しています(IMHO)。

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

これを作成した理由のコンテキストは、このブログ投稿にあります。あなたに似ていると思います。

私は次のように使用しますsetup.py

with stdchannel_redirected(sys.stderr, os.devnull):
    if compiler.has_function('clock_gettime', libraries=['rt']):
        libraries.append('rt')
于 2013-07-19T19:01:46.240 に答える
-2

このスクリプトを Linux ベースのマシンで実行している場合は、次のことができるはずです。

$> ./runscript.py > output.txt
于 2012-06-21T02:45:25.387 に答える