从脚本捕获标准输出?

2025-02-13 08:35:00
admin
原创
37
摘要:问题描述:假设有一个脚本正在执行如下操作:# module writer.py import sys def write(): sys.stdout.write("foobar") 现在假设我想捕获write函数的输出并将其存储在变量中以供进一步处理。简单的解决方案是:# mod...

问题描述:

假设有一个脚本正在执行如下操作:

# module writer.py
import sys

def write():
    sys.stdout.write("foobar")

现在假设我想捕获write函数的输出并将其存储在变量中以供进一步处理。简单的解决方案是:

# module mymodule.py
from writer import write

out = write()
print out.upper()

但这不管用。我想出了另一种解决方案,而且有效,但如果有更好的方法可以解决这个问题,请告诉我。谢谢

import sys
from cStringIO import StringIO

# setup the environment
backup = sys.stdout

# ####
sys.stdout = StringIO()     # capture output
write()
out = sys.stdout.getvalue() # release output
# ####

sys.stdout.close()  # close the stream 
sys.stdout = backup # restore original stdout

print out.upper()   # post processing

解决方案 1:

Python 3.4 + 上,使用contextlib.redirect_stdout上下文管理器:

from contextlib import redirect_stdout
import io

f = io.StringIO()
with redirect_stdout(f):
    help(pow)
s = f.getvalue()

解决方案 2:

设置stdout是一种合理的方法。另一种方法是将其作为另一个进程运行:

import subprocess

proc = subprocess.Popen(["python", "-c", "import writer; writer.write()"], stdout=subprocess.PIPE)
out = proc.communicate()[0]
print out.upper()

解决方案 3:

这是代码的上下文管理器版本。它产生两个值的列表;第一个是 stdout,第二个是 stderr。

import contextlib
@contextlib.contextmanager
def capture():
    import sys
    from cStringIO import StringIO
    oldout,olderr = sys.stdout, sys.stderr
    try:
        out=[StringIO(), StringIO()]
        sys.stdout,sys.stderr = out
        yield out
    finally:
        sys.stdout,sys.stderr = oldout, olderr
        out[0] = out[0].getvalue()
        out[1] = out[1].getvalue()

with capture() as out:
    print 'hi'

解决方案 4:

从 Python 3 开始,您还可以使用sys.stdout.buffer.write()将 (已) 编码的字节字符串写入 stdout (请参阅Python 3 中的 stdout )。当您这样做时,简单的StringIO方法不起作用,因为sys.stdout.encodingnor都不sys.stdout.buffer可用。

从 Python 2.6 开始,您可以使用TextIOBaseAPI,它包含缺少的属性:

import sys
from io import TextIOWrapper, BytesIO

# setup the environment
old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)

# do some writing (indirectly)
write("blub")

# get output
sys.stdout.seek(0)      # jump to the start
out = sys.stdout.read() # read output

# restore stdout
sys.stdout.close()
sys.stdout = old_stdout

# do stuff with the output
print(out.upper())

此解决方案适用于 Python 2 >= 2.6 和 Python 3。请注意,我们sys.stdout.write()仅接受 unicode 字符串和sys.stdout.buffer.write()字节字符串。对于旧代码可能不是这种情况,但对于无需更改即可在 Python 2 和 3 上运行的代码通常如此。

如果您需要支持直接将字节字符串发送到 stdout 而不使用 stdout.buffer 的代码,您可以使用这种变体:

class StdoutBuffer(TextIOWrapper):
    def write(self, string):
        try:
            return super(StdoutBuffer, self).write(string)
        except TypeError:
            # redirect encoded byte strings directly to buffer
            return super(StdoutBuffer, self).buffer.write(string)

您不必设置缓冲区的编码 sys.stdout.encoding,但这在使用此方法测试/比较脚本输出时会有所帮助。

解决方案 5:

或者也许使用已经存在的功能...

from IPython.utils.capture import capture_output

with capture_output() as c:
    print('some output')

c()

print c.stdout

解决方案 6:

这是我的原始代码的装饰器对应部分。

writer.py保持不变:

import sys

def write():
    sys.stdout.write("foobar")

mymodule.py稍作修改:

from writer import write as _write
from decorators import capture

@capture
def write():
    return _write()

out = write()
# out post processing...

这是装饰器:

def capture(f):
    """
    Decorator to capture standard output
    """
    def captured(*args, **kwargs):
        import sys
        from cStringIO import StringIO

        # setup the environment
        backup = sys.stdout

        try:
            sys.stdout = StringIO()     # capture output
            f(*args, **kwargs)
            out = sys.stdout.getvalue() # release output
        finally:
            sys.stdout.close()  # close the stream 
            sys.stdout = backup # restore original stdout

        return out # captured output wrapped in a string

    return captured

解决方案 7:

这是一个上下文管理器,它从@JonnyJD 的答案中获得灵感,支持将字节写入buffer属性,同时还利用sys 的 dunder-io 引用来进一步简化。

import io
import sys
import contextlib


@contextlib.contextmanager
def capture_output():
    output = {}
    try:
        # Redirect
        sys.stdout = io.TextIOWrapper(io.BytesIO(), sys.stdout.encoding)
        sys.stderr = io.TextIOWrapper(io.BytesIO(), sys.stderr.encoding)
        yield output
    finally:
        # Read
        sys.stdout.seek(0)
        sys.stderr.seek(0)
        output['stdout'] = sys.stdout.read()
        output['stderr'] = sys.stderr.read()
        sys.stdout.close()
        sys.stderr.close()

        # Restore
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__


with capture_output() as output:
    print('foo')
    sys.stderr.buffer.write(b'bar')

print('stdout: {stdout}'.format(stdout=output['stdout']))
print('stderr: {stderr}'.format(stderr=output['stderr']))

输出为:

stdout: foo

stderr: bar

解决方案 8:

这里的问题(如何重定向输出的示例,而不是部分tee)用于os.dup2在操作系统级别重定向流。这很好,因为它也适用于您从程序中生成的命令。

解决方案 9:

我认为您应该看看以下四个对象:

from test.test_support import captured_stdout, captured_output, \n    captured_stderr, captured_stdin

例子:

from writer import write

with captured_stdout() as stdout:
    write()
print stdout.getvalue().upper()

UPD:正如 Eric 在评论中所说,不应该直接使用它们,所以我复制并粘贴了它。

# Code from test.test_support:
import contextlib
import sys

@contextlib.contextmanager
def captured_output(stream_name):
    """Return a context manager used by captured_stdout and captured_stdin
    that temporarily replaces the sys stream *stream_name* with a StringIO."""
    import StringIO
    orig_stdout = getattr(sys, stream_name)
    setattr(sys, stream_name, StringIO.StringIO())
    try:
        yield getattr(sys, stream_name)
    finally:
        setattr(sys, stream_name, orig_stdout)

def captured_stdout():
    """Capture the output of sys.stdout:

       with captured_stdout() as s:
           print "hello"
       self.assertEqual(s.getvalue(), "hello")
    """
    return captured_output("stdout")

def captured_stderr():
    return captured_output("stderr")

def captured_stdin():
    return captured_output("stdin")

解决方案 10:

我喜欢 contextmanager 解决方案,但是如果您需要使用打开文件和 fileno 支持存储的缓冲区,您可以做这样的事情。

import six
from six.moves import StringIO


class FileWriteStore(object):
    def __init__(self, file_):
        self.__file__ = file_
        self.__buff__ = StringIO()

    def __getattribute__(self, name):
        if name in {
            "write", "writelines", "get_file_value", "__file__",
                "__buff__"}:
            return super(FileWriteStore, self).__getattribute__(name)
        return self.__file__.__getattribute__(name)

    def write(self, text):
        if isinstance(text, six.string_types):
            try:
                self.__buff__.write(text)
            except:
                pass
        self.__file__.write(text)

    def writelines(self, lines):
        try:
            self.__buff__.writelines(lines)
        except:
            pass
        self.__file__.writelines(lines)

    def get_file_value(self):
        return self.__buff__.getvalue()

使用

import sys
sys.stdout = FileWriteStore(sys.stdout)
print "test"
buffer = sys.stdout.get_file_value()
# you don't want to print the buffer while still storing
# else it will double in size every print
sys.stdout = sys.stdout.__file__
print buffer

解决方案 11:

当第三方代码已经复制了引用时的另一种方法sys.stdout是暂时替换write()方法本身:

from types import MethodType
...
f = io.StringIO()
def new_write(self, data):
    f.write(data)

old_write = sys.stdout.write
sys.stdout.write = MethodType(new_write, sys.stdout)
error = command.run(args)
sys.stdout.write = old_write
output = f.getvalue()

解决方案 12:

认为@arthur的评论应该存在于答案中。

使用check_output以下方法subprocess似乎最简单:

In [1]: import subprocess
   ...: 
   ...: command = "echo 'hello world'"
   ...: output = subprocess.check_output(command, shell=True, encoding='utf-8')

In [2]: print(output)
hello world
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用