스크립트에서 stdout을 캡처하시겠습니까?
다음과 같은 작업을 수행하는 스크립트가 있다고 가정합니다.
# 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
Python 3.4+에서 컨텍스트 관리자를 사용합니다.
from contextlib import redirect_stdout
import io
f = io.StringIO()
with redirect_stdout(f):
help(pow)
s = f.getvalue()
설정stdout
그것은 합리적인 방법입니다.또 다른 방법은 다른 프로세스로 실행하는 것입니다.
import subprocess
proc = subprocess.Popen(["python", "-c", "import writer; writer.write()"], stdout=subprocess.PIPE)
out = proc.communicate()[0]
print out.upper()
코드의 컨텍스트 관리자 버전입니다.첫 번째 값은 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'
Python 3부터 사용할 수 있습니다.sys.stdout.buffer.write()
(이미) 인코딩된 바이트 문자열을 stdout에 씁니다(Python 3의 stdout 참조).그렇게 할 때, 단순한 것은StringIO
접근법은 둘 다 효과가 없기 때문에 작동하지 않습니다.sys.stdout.encoding
도 아니다sys.stdout.buffer
가능할 것 같습니다.
Python 2.6부터는 누락된 특성이 포함된 API를 사용할 수 있습니다.
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()
유니코드 문자열만 허용하고sys.stdout.buffer.write()
바이트 문자열만 허용합니다.이전 코드의 경우에는 그렇지 않을 수 있지만 변경 없이 Python 2 및 3에서 실행되도록 빌드된 코드의 경우에는 종종 해당됩니다.
stdout.buffer를 사용하지 않고 직접 stdout으로 바이트 문자열을 보내는 코드를 지원해야 하는 경우 다음 변형을 사용할 수 있습니다.
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 버퍼의 인코딩을 설정할 필요는 없지만 스크립트 출력을 테스트/비교하기 위해 이 방법을 사용할 때 도움이 됩니다.
또는 이미 존재하는 기능을 사용할 수도 있습니다.
from IPython.utils.capture import capture_output
with capture_output() as c:
print('some output')
c()
print c.stdout
이것은 제 원래 코드의 장식자입니다.
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
여기 @Jonny에서 영감을 얻은 상황 관리자가 있습니다.바이트 쓰기를 지원하는 JD의 답변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
여기서의 질문은 (출력을 리디렉션하는 방법의 예이지, 출력을 리디렉션하는 방법의 예는 아닙니다.tee
부분) 용도os.dup2
OS 수준에서 스트림을 리디렉션합니다.프로그램에서 생성하는 명령에도 적용되므로 유용합니다.
저는 당신이 이 네 가지 물건을 살펴봐야 한다고 생각합니다.
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()
업데이트: 에릭이 댓글에서 말한 것처럼 직접 사용하면 안되기 때문에 복사해서 붙였습니다.
# 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")
저는 컨텍스트 관리자 솔루션이 마음에 들지만, 열려 있는 파일에 저장된 버퍼가 필요하고 지원이 없으면 이와 같은 작업을 수행할 수 있습니다.
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
에 대한 방법입니다.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()
@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
언급URL : https://stackoverflow.com/questions/5136611/capture-stdout-from-a-script
'programing' 카테고리의 다른 글
Unity의 통화당 싱글톤 컨텍스트(웹 요청) (0) | 2023.08.02 |
---|---|
Gradle | 스프링 부트 종속성은 제외되지 않습니다. (0) | 2023.08.02 |
스프링 부트 - 키클로크를 비활성화하는 방법은 무엇입니까? (0) | 2023.08.02 |
처음과 마지막 큰따옴표를 어떻게 벗길 수 있습니까? (0) | 2023.08.02 |
UI 도구 모음에 UI 레이블 추가 (0) | 2023.08.02 |