파이썬에서 stdout을 임의의 파일로 어떻게 리디렉션합니까?
ssh 세션 내에서 오래 실행되는 Python 스크립트 (예 : 웹 응용 프로그램)가 시작되고 백 바운드가 발생하고 ssh 세션이 닫히면 응용 프로그램에서 IOError가 발생하고 stdout에 쓰려고하는 순간 실패합니다. IOError로 인한 오류를 방지하기 위해 응용 프로그램 및 모듈을 stdout이 아닌 파일로 출력하는 방법을 찾아야했습니다. 현재 nohup을 사용하여 출력을 파일로 리디렉션하면 작업이 완료되지만 호기심으로 nohup을 사용하지 않고 수행 할 수있는 방법이 있는지 궁금합니다.
이미 시도 sys.stdout = open('somefile', 'w')
했지만 이것이 일부 외부 모듈이 여전히 터미널로 출력되는 것을 막지 않는 것 같습니다 (또는 sys.stdout = ...
회선이 전혀 발생하지 않았을 수도 있음 ). 테스트 한 간단한 스크립트에서 작동해야한다는 것을 알고 있지만 아직 웹 응용 프로그램에서 테스트 할 시간이 없었습니다.
답변
Python 스크립트 내에서 리디렉션을 수행하려면 sys.stdout
파일 객체로 설정 하면 트릭이 수행됩니다.
import sys
sys.stdout = open('file', 'w')
print('test')
훨씬 일반적인 방법은 실행할 때 셸 리디렉션을 사용하는 것입니다 (Windows 및 Linux에서 동일).
$ python foo.py > file
답변
파이썬 3.4 에는 contextlib.redirect_stdout()
함수 가 있습니다 :
from contextlib import redirect_stdout
with open('help.txt', 'w') as f:
with redirect_stdout(f):
print('it now prints to `help.text`')
다음과 유사합니다.
import sys
from contextlib import contextmanager
@contextmanager
def redirect_stdout(new_target):
old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout
try:
yield new_target # run some code with the replaced stdout
finally:
sys.stdout = old_target # restore to the previous value
이전 파이썬 버전에서 사용할 수 있습니다. 후자의 버전은 재사용 할 수 없습니다 . 원하는 경우 하나 만들 수 있습니다.
파일 디스크립터 레벨에서 stdout을 리디렉션하지 않습니다. 예 :
import os
from contextlib import redirect_stdout
stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, redirect_stdout(f):
print('redirected to a file')
os.write(stdout_fd, b'not redirected')
os.system('echo this also is not redirected')
b'not redirected'
그리고 'echo this also is not redirected'
받는 리디렉션되지 않습니다 output.txt
파일.
파일 디스크립터 레벨에서 경로 재 지정하려면 os.dup2()
다음을 사용할 수 있습니다.
import os
import sys
from contextlib import contextmanager
def fileno(file_or_fd):
fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)()
if not isinstance(fd, int):
raise ValueError("Expected a file (`.fileno()`) or a file descriptor")
return fd
@contextmanager
def stdout_redirected(to=os.devnull, stdout=None):
if stdout is None:
stdout = sys.stdout
stdout_fd = fileno(stdout)
# copy stdout_fd before it is overwritten
#NOTE: `copied` is inheritable on Windows when duplicating a standard stream
with os.fdopen(os.dup(stdout_fd), 'wb') as copied:
stdout.flush() # flush library buffers that dup2 knows nothing about
try:
os.dup2(fileno(to), stdout_fd) # $ exec >&to
except ValueError: # filename
with open(to, 'wb') as to_file:
os.dup2(to_file.fileno(), stdout_fd) # $ exec > to
try:
yield stdout # allow code to be run with the redirected stdout
finally:
# restore stdout to its previous value
#NOTE: dup2 makes stdout_fd inheritable unconditionally
stdout.flush()
os.dup2(copied.fileno(), stdout_fd) # $ exec >&copied
다음 stdout_redirected()
대신에를 사용 하면 동일한 예제가 작동합니다 redirect_stdout()
.
import os
import sys
stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, stdout_redirected(f):
print('redirected to a file')
os.write(stdout_fd, b'it is redirected now\n')
os.system('echo this is also redirected')
print('this is goes back to stdout')
이전에 stdout에 인쇄 된 출력은 컨텍스트 관리자가 활성화 output.txt
되어있는 한 계속 진행됩니다 stdout_redirected()
.
참고 : stdout.flush()
하지 않습니다 I / O가 직접 구현 파이썬 3에 플러시 C 표준 입출력 버퍼 read()
/ write()
시스템 호출. 열려있는 모든 C stdio 출력 스트림을 플러시하기 위해 libc.fflush(None)
일부 C 확장에서 stdio 기반 I / O를 사용하는 경우 명시 적으로 호출 할 수 있습니다 .
try:
import ctypes
from ctypes.util import find_library
except ImportError:
libc = None
else:
try:
libc = ctypes.cdll.msvcrt # Windows
except OSError:
libc = ctypes.cdll.LoadLibrary(find_library('c'))
def flush(stream):
try:
libc.fflush(None)
stream.flush()
except (AttributeError, ValueError, IOError):
pass # unsupported
stdout
매개 변수를 사용 하여 다른 스트림을 리디렉션 할 수 있습니다 ( sys.stdout
예 : 병합 sys.stderr
및 🙂 sys.stdout
.
def merged_stderr_stdout(): # $ exec 2>&1
return stdout_redirected(to=sys.stdout, stdout=sys.stderr)
예:
from __future__ import print_function
import sys
with merged_stderr_stdout():
print('this is printed on stdout')
print('this is also printed on stdout', file=sys.stderr)
참고 : stdout_redirected()
버퍼 된 I / O ( sys.stdout
보통)와 버퍼되지 않은 I / O (파일 설명자에 대한 직접 작업)를 혼합 합니다. 버퍼링 문제 가있을 수 있습니다 .
대답 : 편집 : python-daemon
스크립트를 데몬logging
( demonize )하고 print
명령문 대신 모듈을 사용 하여 @ erikb85가 제안한 대로 사용할 수 있으며 nohup
지금 사용하는 오래 실행되는 Python 스크립트의 stdout을 리디렉션합니다 .
답변
당신은 이것을 훨씬 더 잘 시도 할 수 있습니다
import sys
class Logger(object):
def __init__(self, filename="Default.log"):
self.terminal = sys.stdout
self.log = open(filename, "a")
def write(self, message):
self.terminal.write(message)
self.log.write(message)
sys.stdout = Logger("yourlogfilename.txt")
print "Hello world !" # this is should be saved in yourlogfilename.txt
답변
다른 답변은 갈래 프로세스가 새 stdout을 공유하려는 경우에는 다루지 않았습니다.
하기 위해서:
from os import open, close, dup, O_WRONLY
old = dup(1)
close(1)
open("file", O_WRONLY) # should open on 1
..... do stuff and then restore
close(1)
dup(old) # should dup to 1
close(old) # get rid of left overs
답변
PEP 343 에서 인용 – “with”명세서 (가져온 명세서) :
stdout을 임시로 리디렉션하십시오.
import sys
from contextlib import contextmanager
@contextmanager
def stdout_redirected(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys.stdout = save_stdout
다음과 같이 사용됩니다 :
with open(filename, "w") as f:
with stdout_redirected(f):
print "Hello world"
이것은 물론 스레드 안전하지는 않지만 수동으로 동일한 춤을 수행하지는 않습니다. 단일 스레드 프로그램 (예 : 스크립트)에서는 널리 사용되는 방법입니다.
답변
import sys
sys.stdout = open('stdout.txt', 'w')
답변
다음은 Yuda Prawira 답변 의 변형입니다 .
- 구현
flush()
및 모든 파일 속성 - 컨텍스트 관리자로 작성
- 캡처
stderr
도
.
import contextlib, sys
@contextlib.contextmanager
def log_print(file):
# capture all outputs to a log file while still printing it
class Logger:
def __init__(self, file):
self.terminal = sys.stdout
self.log = file
def write(self, message):
self.terminal.write(message)
self.log.write(message)
def __getattr__(self, attr):
return getattr(self.terminal, attr)
logger = Logger(file)
_stdout = sys.stdout
_stderr = sys.stderr
sys.stdout = logger
sys.stderr = logger
try:
yield logger.log
finally:
sys.stdout = _stdout
sys.stderr = _stderr
with log_print(open('mylogfile.log', 'w')):
print('hello world')
print('hello world on stderr', file=sys.stderr)
# you can capture the output to a string with:
# with log_print(io.StringIO()) as log:
# ....
# print('[captured output]', log.getvalue())