misalkan ada skrip yang melakukan hal seperti ini:
# module writer.py
import sys
def write():
sys.stdout.write("foobar")
Sekarang misalkan saya ingin menangkap output dari write
fungsi dan menyimpannya dalam variabel untuk diproses lebih lanjut. Solusi naifnya adalah:
# module mymodule.py
from writer import write
out = write()
print out.upper()
Tapi ini tidak berhasil. Saya datang dengan solusi lain dan berhasil, tapi tolong, beri tahu saya jika ada cara yang lebih baik untuk menyelesaikan masalah. Terima kasih
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
value = subprocess.check_output(command, shell=True)
Berikut adalah versi pengelola konteks dari kode Anda. Ini menghasilkan daftar dua nilai; yang pertama adalah stdout, yang kedua adalah 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'
sumber
with capture() as out:
akan berperilaku berbeda denganwith capture() as out, err:
subprocess
dan mengalihkan keluaran ke sys.stdout / stderr. Ini karenaStringIO
bukan objek file nyata dan melewatkanfileno()
fungsinya.Untuk pengunjung di masa mendatang: Contextlib Python 3.4 menyediakan ini secara langsung (lihat bantuan contextlib Python ) melalui
redirect_stdout
pengelola konteks:from contextlib import redirect_stdout import io f = io.StringIO() with redirect_stdout(f): help(pow) s = f.getvalue()
sumber
Ini adalah bagian dekorator dari kode asli saya.
writer.py
tetap sama:import sys def write(): sys.stdout.write("foobar")
mymodule.py
sligthly dimodifikasi:from writer import write as _write from decorators import capture @capture def write(): return _write() out = write() # out post processing...
Dan inilah dekoratornya:
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
sumber
Atau mungkin menggunakan fungsionalitas yang sudah ada ...
from IPython.utils.capture import capture_output with capture_output() as c: print('some output') c() print c.stdout
sumber
Dimulai dengan Python 3 Anda juga dapat menggunakan
sys.stdout.buffer.write()
untuk menulis (sudah) string byte yang dikodekan ke stdout (lihat stdout di Python 3 ). Ketika Anda melakukan itu,StringIO
pendekatan sederhana tidak berhasil karena tidaksys.stdout.encoding
jugasys.stdout.buffer
akan tersedia.Dimulai dengan Python 2.6 Anda dapat menggunakan
TextIOBase
API , yang menyertakan atribut yang hilang: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())
Solusi ini bekerja untuk Python 2> = 2.6 dan Python 3. Harap dicatat bahwa kami
sys.stdout.write()
hanya menerima string unicode dansys.stdout.buffer.write()
hanya menerima string byte. Ini mungkin bukan kasus untuk kode lama, tetapi sering kali kasus kode yang dibuat untuk berjalan di Python 2 dan 3 tanpa perubahan.Jika Anda perlu mendukung kode yang mengirimkan string byte ke stdout secara langsung tanpa menggunakan stdout.buffer, Anda dapat menggunakan variasi ini:
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)
Anda tidak perlu menyetel pengkodean buffer sys.stdout.encoding, tetapi ini membantu saat menggunakan metode ini untuk menguji / membandingkan keluaran skrip.
sumber
Pertanyaan di sini (contoh bagaimana mengarahkan keluaran, bukan
tee
bagian) digunakanos.dup2
untuk mengarahkan aliran pada tingkat OS. Itu bagus karena ini akan berlaku untuk perintah yang Anda buat dari program Anda juga.sumber
Saya pikir Anda harus melihat empat objek ini:
from test.test_support import captured_stdout, captured_output, \ captured_stderr, captured_stdin
Contoh:
from writer import write with captured_stdout() as stdout: write() print stdout.getvalue().upper()
UPD: Seperti yang Eric katakan dalam komentar, seseorang tidak boleh menggunakannya secara langsung, jadi saya menyalin dan menempelkannya.
# 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")
sumber
Saya suka solusi contextmanager namun jika Anda membutuhkan buffer yang disimpan dengan file terbuka dan dukungan fileno, Anda dapat melakukan sesuatu seperti ini.
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()
menggunakan
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
sumber
Berikut ini adalah manajer konteks yang mengambil inspirasi dari jawaban @ JonnyJD yang mendukung penulisan byte ke
buffer
atribut tetapi juga memanfaatkan referensi dunder -io sys untuk penyederhanaan lebih lanjut.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']))
Outputnya adalah:
sumber