스크립트에서 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
설정 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.4 contextlib는 컨텍스트 관리자 를 통해 이를 직접 제공합니다 ( Python contextlib 도움말 참조 ) redirect_stdout
.
from contextlib import redirect_stdout
import io
f = io.StringIO()
with redirect_stdout(f):
help(pow)
s = f.getvalue()
또는 이미 존재하는 기능을 사용할 수도 있습니다.
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
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
Python 3부터는 sys.stdout.buffer.write()
(이미) 인코딩 된 바이트 문자열을 stdout에 쓰는 데 사용할 수도 있습니다 ( Python 3의 stdout 참조 ). 당신이 작업을 수행 할 때 간단한 StringIO
어느 쪽도이 때문에 접근이 작동하지 않습니다 sys.stdout.encoding
도 sys.stdout.buffer
사용할 수 없을 것입니다.
Python 2.6부터는 누락 된 속성이 포함 된 TextIOBase
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에서 실행되도록 빌드 된 코드의 경우가 많습니다.
If you need to support code that sends byte strings to stdout directly without using stdout.buffer, you can use this variation:
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)
You don't have to set the encoding of the buffer the sys.stdout.encoding, but this helps when using this method for testing/comparing script output.
The question here (the example of how to redirect output, not the tee
part) uses os.dup2
to redirect a stream at the OS level. That is nice because it will apply to commands that you spawn from your program as well.
I think You should look at these four objects:
from test.test_support import captured_stdout, captured_output, \
captured_stderr, captured_stdin
Example:
from writer import write
with captured_stdout() as stdout:
write()
print stdout.getvalue().upper()
UPD: As Eric said in a comment, one shouldn't use they directly, so I copied and pasted it.
# 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")
I like the contextmanager solution however if you need the buffer stored with the open file and fileno support you could do something like this.
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()
use
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
참고URL : https://stackoverflow.com/questions/5136611/capture-stdout-from-a-script
'program tip' 카테고리의 다른 글
람다를 키에 바인딩 할 때 "잘못된 형식 인수 : commandp"오류 (0) | 2020.10.12 |
---|---|
다른 줄에있는 파일의 여러 문자열에 대한 grep (예 : 줄 기반 검색이 아닌 전체 파일)? (0) | 2020.10.12 |
추천하는 Javascript 템플릿 엔진은 무엇입니까? (0) | 2020.10.12 |
포인터 주소와 포인터 값을 증가시키는 방법은 무엇입니까? (0) | 2020.10.12 |
Spark-앱을 제출할 때 "구성에 마스터 URL을 설정해야합니다"오류 (0) | 2020.10.11 |