programing

stdout을 Python의 파일로 리디렉션하시겠습니까?

bestcode 2022. 11. 6. 10:36
반응형

stdout을 Python의 파일로 리디렉션하시겠습니까?

Python에서 stdout을 임의의 파일로 리다이렉트하려면 어떻게 해야 하나요?

장시간 실행되는 Python 스크립트(예: 웹 응용 프로그램)가 ssh 세션 내에서 시작되어 백고딩되고 ssh 세션이 닫히면 응용 프로그램은 IOError를 발생시키고 stdout에 쓰려고 하면 실패합니다.IOError로 인한 장애를 방지하기 위해 애플리케이션과 모듈을 stdout이 아닌 파일로 출력할 수 있는 방법을 찾아야 했습니다.현재는 파일로 출력을 리다이렉트 하는 nohup을 사용하고 있습니다만, 궁금해서 nohup을 사용하지 않고 할 수 있는 방법이 없을까요?

나는 이미 시도했다.sys.stdout = open('somefile', 'w')단, 일부 외부 모듈이 아직 단말기에 출력되지 않도록 하는 것은 아닌 것 같습니다(또는sys.stdout = ...회선은 전혀 발화하지 않았다).테스트한 간단한 스크립트에서 동작해야 한다는 것은 알지만 아직 웹 어플리케이션에서 테스트해 볼 시간은 없었습니다.

Python 스크립트 내에서 리다이렉션을 수행하려면sys.stdout파일 오브젝트에는 다음과 같은 트릭이 있습니다.

# for python3
import sys
with open(‘file’, ‘w’) as sys.stdout:
    print('test')

훨씬 일반적인 방법은 실행 시 셸 리다이렉션을 사용하는 것입니다(Windows 및 Linux에서 동일).

$ python3 foo.py > file

Python 3.4+에는 다음과 같은 기능이 있습니다.

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

이전 Python 버전에서 사용할 수 있습니다.후자의 버전은 재사용할 수 없습니다.원한다면 하나로 만들 수 있다.

다음과 같이 파일 기술자 수준에서 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가 직접 구현되어 있는 Python 3에서는 C stdio 버퍼를 플래시하지 않습니다.read()/write()시스템 콜열려 있는 모든 Cstdio 출력 스트림을 플러시하려면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(파일 기술자에 대한 직접 작업)를 제공합니다.버퍼링 문제가 있을 수 있으니 주의하세요.

답변: 편집: 를 사용하여 스크립트를 데몬화하고loggingmodule(@moduleb85에서 권장하는 바와 같이) 대신printstdout을 사용하여 만으로 스테이트먼저 Python 스크립트는 Python으로 리다이렉트 할 수 있습니다.nohup

너는 이것을 너무 더 잘 할 수 있다.

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" 스테이트먼트(추가된 Import 스테이트먼트):

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

tmux 또는 GNU 화면과 같은 터미널 멀티플렉서가 필요합니다.

Python의 속임수가 얼마나 영리하고 얼마나 많은 업베이트를 받았는지에 관계없이 Ryan Amos의 첫 번째 질문에 대한 작은 코멘트가 제공되는 다른 모든 솔루션보다 훨씬 더 바람직한 솔루션에 대한 유일한 언급이라는 사실에 놀랐습니다.Ryan의 코멘트에 덧붙여, Tmux는 GNU 화면의 훌륭한 대체 수단입니다.

그러나 원칙은 동일합니다.로그아웃 중에 터미널 작업을 실행하고 싶은 경우, 카페에 가서 샌드위치를 먹고, 화장실에 가고, 집에 가고 싶은 경우(등), 그 후, 외출한 적이 없는 것처럼 어디에서든 또는 임의의 컴퓨터에서 터미널 세션에 재접속할 수 있습니다.VNC 또는 터미널 세션용 리모트데스크탑이라고 생각하시면 됩니다그 외의 것은 회피책입니다.보너스로, 상사나 파트너가 접속했을 때, 브라우저의 의심스러운 컨텐츠가 있는 창이 아니라, 부주의로 터미널 창을 ctrl-w/cmd-w로 눌렀을 경우, 지난 18시간의 처리 시간을 잃지 않습니다.

이 답변에 근거해, https://stackoverflow.com/a/5916874/1060344,는, 제가 제 프로젝트 중 하나에서 사용하는 것을 알아내는 또 다른 방법입니다.대체품이 무엇이든지sys.stderr ★★★★★★★★★★★★★★★★★」sys.stdout경우 교체가 .file특히 stderr/stdout이 사용자가 제어할 수 없는 다른 라이브러리에서 사용되고 있기 때문에 이 작업이 수행 중인 경우.해당 라이브러리는 파일 객체의 다른 메서드를 사용하고 있을 수 있습니다.

이 방법을 통해 모든 것이 stderr/stdout(또는 그 문제의 파일)을 실행하도록 허용하고 Python의 로깅 기능을 사용하여 로그 파일에 메시지를 보낼 수 있습니다(그러나 이 방법을 사용하여 실제로 어떤 작업도 수행할 수 있습니다).

class FileToLogInterface(file):
    '''
    Interface to make sure that everytime anything is written to stderr, it is
    also forwarded to a file.
    '''

    def __init__(self, *args, **kwargs):
        if 'cfg' not in kwargs:
            raise TypeError('argument cfg is required.')
        else:
            if not isinstance(kwargs['cfg'], config.Config):
                raise TypeError(
                    'argument cfg should be a valid '
                    'PostSegmentation configuration object i.e. '
                    'postsegmentation.config.Config')
        self._cfg = kwargs['cfg']
        kwargs.pop('cfg')

        self._logger = logging.getlogger('access_log')

        super(FileToLogInterface, self).__init__(*args, **kwargs)

    def write(self, msg):
        super(FileToLogInterface, self).write(msg)
        self._logger.info(msg)

다른 언어(예를 들어 C)로 작성된 프로그램에서는 터미널에서 분리(및 좀비 프로세스 방지)하기 위해 특별한 마술(이중 포킹)을 수행해야 합니다.그래서 나는 그들을 모방하는 것이 가장 좋은 해결책이라고 생각한다.

할 수 "Directions")./usr/bin/python mycoolscript.py 2>&1 1>/dev/null

상세한 것에 대하여는, 다음의 투고를 참조해 주세요.데몬을 작성할 때 이중 포크를 실행하는 이유는 무엇입니까?

답을 알고 이 질문에 대한 답은 다음과 같습니다).python abc.py > output.log 2>&1 는, 아직 필요가 하지만, 다음과 같이 말할 필요가 있습니다.

프로그램을 작성할 때 stdout에 쓰지 마십시오.원하는 것을 출력하려면 항상 로깅을 사용하십시오.이것에 의해, 향후, 출력 파일을 리다이렉트, 필터링, 회전할 때, 많은 자유를 얻을 수 있습니다.

@jfs에서 설명한 바와 같이 대부분의 솔루션은 C 확장으로부터의 stdout 출력과 같은 일부 유형의 stdout 출력을 적절하게 처리하지 않습니다..PI 서 pi pi pi pi pi pi pi pi pi pi pi pi pi pi 。wurlitzer하죠.sys_pipes★★★★★★★★★ 도 간단합니다다음과 같이 간단하게 사용할 수 있습니다.

from contextlib import redirect_stdout
import os
from wurlitzer import sys_pipes
        
log = open("test.log", "a")
with redirect_stdout(log), sys_pipes():
    print("print statement")
    os.system("echo echo call")

이 투고에서의 이전의 회답에 근거해, 코드 조각의 출력을 리스트로 리다이렉트 해, 그 후에 출력이 정규화되도록 하는, 보다 콤팩트하고 유연한 방법으로 이 클래스를 작성했습니다.

class out_to_lt():
    def __init__(self, lt):
        if type(lt) == list:
            self.lt = lt
        else:
            raise Exception("Need to pass a list")            
    def __enter__(self):
        import sys
        self._sys = sys
        self._stdout = sys.stdout
        sys.stdout = self
        return self
    def write(self,txt):
        self.lt.append(txt)    
    def __exit__(self, type, value, traceback):
        self._sys.stdout = self._stdout

사용처:

lt = []
with out_to_lt(lt) as o:
    print("Test 123\n\n")
    print(help(str))

업데이트 중입니다.두 가지 방법을 추가해야 하지만 쉽게 적응할 수 있는 시나리오를 찾았습니다.

class out_to_lt():
    ...
    def isatty(self):
        return True #True: You're running in a real terminal, False:You're being piped, redirected, cron
    def flush(self):
        pass

콘텍스트를 사용하는 다른 버전은 있지만 이렇게 간단한 버전은 없습니다.실제로 구글에 접속하여 동작하는 것을 확인했지만 보이지 않아 놀랐습니다.따라서 안전하고 컨텍스트 블록 내의 코드만을 대상으로 한 빠른 솔루션을 찾고 있는 다른 사용자는 다음과 같습니다.

import sys
with open('test_file', 'w') as sys.stdout:
    print('Testing 1 2 3')

다음과 같이 테스트:

$ cat redirect_stdout.py
import sys

with open('test_file', 'w') as sys.stdout:
    print('Testing 1 2 3')
$ python redirect_stdout.py
$ cat test_file
Testing 1 2 3

언급URL : https://stackoverflow.com/questions/4675728/redirect-stdout-to-a-file-in-python

반응형