स्क्रिप्ट से स्टैडआउट कैप्चर करें?


89

मान लीजिए एक स्क्रिप्ट कुछ इस तरह से है:

# 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

जवाबों:


49

सेटिंग करना stdoutएक उचित तरीका है। एक और इसे एक और प्रक्रिया के रूप में चलाने के लिए है:

import subprocess

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

4
check_output सीधे उपप्रोसेस में चलने वाले कमांड के आउटपुट को कैप्चर करता है: <br> मान = subprocess.check_output (कमांड, शेल = ट्रू)
आर्थर

1
प्रारूपित संस्करण :value = subprocess.check_output(command, shell=True)
Nae

45

यहां आपके कोड का एक संदर्भ प्रबंधक संस्करण है। यह दो मूल्यों की एक सूची देता है; पहला 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'

इस घोल से प्यार करें। मैंने संशोधित किया है, इसलिए गलती से उस धारा से सामान खोना नहीं है जिस पर मैं आउटपुट की उम्मीद नहीं कर रहा हूं, जैसे अप्रत्याशित त्रुटियां। मेरे मामले में, कैप्चर () sys.stderr या sys.stdout को एक पैरामीटर के रूप में स्वीकार कर सकता है, जो केवल उस स्ट्रीम पर कब्जा करने का संकेत देता है।
जोशुआ रिचर्डसन 23

StringIO किसी भी फैशन में यूनिकोड का समर्थन नहीं करता है, इसलिए आप उपरोक्त समर्थन को गैर-ASCII वर्ण बनाने के लिए यहाँ उत्तर को एकीकृत कर सकते हैं: stackoverflow.com/a/1819009/425050
mafrosis

2
अंत में एक उपज मूल्य को संशोधित करना वास्तव में अजीब है - with capture() as out:अलग तरीके से व्यवहार करेगाwith capture() as out, err:
एरिक

Io मॉड्यूल का उपयोग करके यूनिकोड / stdout.buffer समर्थन तक पहुंचा जा सकता है। मेरा जवाब देखिए ।
जॉनीजेड

1
यदि आप उपयोग करते हैं subprocessऔर आउटपुट को sys.stdout / stderr पर पुनर्निर्देशित करते हैं तो यह समाधान टूट जाता है । ऐसा इसलिए है क्योंकि StringIOकोई वास्तविक फ़ाइल ऑब्जेक्ट नहीं है और fileno()फ़ंक्शन को याद नहीं करता है।
लेटैमिक नोव

44

भविष्य के आगंतुकों के लिए: संदर्भ प्रबंधक के माध्यम से सीधे पायथन 3.4 रेफ़रलिब इसके लिए प्रावधान करता है ( पायथन रेफ़रलिब मदद देखें ) redirect_stdout:

from contextlib import redirect_stdout
import io

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

जब sys.stdout.buffer (जैसा कि आपको बाइट लिखते समय करने की आवश्यकता है) लिखने की कोशिश करते समय यह समस्या हल नहीं होती है। स्ट्रिंगआईआईओ में बफर विशेषता नहीं है, जबकि टेक्स्टआईओवॉपर करता है। @JonnyJD से जवाब देखें।
बुनकर

9

यह मेरे मूल कोड का डेकोरेटर समकक्ष है।

writer.py एक ही रहता है:

import sys

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

mymodule.py sligthly संशोधित हो जाता है:

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

9

या हो सकता है कि पहले से ही कार्यक्षमता का उपयोग करें ...

from IPython.utils.capture import capture_output

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

c()

print c.stdout

7

पायथन 3 से शुरू करके आप sys.stdout.buffer.write()stdout को (पहले से) एन्कोडेड बाइट स्ट्रिंग्स को लिखने के लिए उपयोग कर सकते हैं ( पायथन 3 में स्टडआउट देखें )। जब आप ऐसा करते हैं, तो सरल StringIOतरीका काम नहीं करता है क्योंकि न तो उपलब्ध होगा sys.stdout.encodingऔर न ही sys.stdout.bufferउपलब्ध होगा।

पायथन 2.6 के साथ शुरू आप TextIOBaseएपीआई का उपयोग कर सकते हैं , जिसमें लापता गुण शामिल हैं:

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

यह समाधान पायथन 2> = 2.6 और पायथन 3 के लिए काम करता है। कृपया ध्यान दें कि हमारा sys.stdout.write()एकमात्र यूनिकोड स्ट्रिंग्स को sys.stdout.buffer.write()स्वीकार करता है और केवल बाइट स्ट्रिंग्स को स्वीकार करता है। यह पुराने कोड के लिए मामला नहीं हो सकता है, लेकिन अक्सर कोड के लिए मामला है जो कि बदलाव के बिना पायथन 2 और 3 पर चलने के लिए बनाया गया है।

यदि आपको 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 को सेट करने की आवश्यकता नहीं है।


3

सवाल यहाँ (कैसे उत्पादन अनुप्रेषित करने के उदाहरण के लिए, नहीं teeभाग) का उपयोग करता है os.dup2ओएस स्तर पर एक धारा को अंतरित करें। यह अच्छा है क्योंकि यह उन आदेशों पर लागू होगा जो आप अपने कार्यक्रम से भी करते हैं।


3

मुझे लगता है कि आपको इन चार वस्तुओं को देखना चाहिए:

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

उदाहरण:

from writer import write

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

UPD: जैसा कि एरिक ने एक टिप्पणी में कहा था, किसी को सीधे उपयोग नहीं करना चाहिए, इसलिए मैंने इसे कॉपी और पेस्ट किया।

# 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")

3

मुझे प्रसंग समाधान पसंद है, लेकिन अगर आपको खुली फाइल और फिलीनो समर्थन के साथ संग्रहीत बफर की आवश्यकता है तो आप ऐसा कुछ कर सकते हैं।

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

0

यहाँ एक संदर्भ प्रबंधक ने @ जॉनीजेड के उत्तर का समर्थन करते हुए लेखन बाइट्स से प्रेरणा लेते हुए कहा है कि आगे सरलीकरण के लिए sys के dunder-io referenes काbuffer लाभ उठाना भी शामिल है ।

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.