Saltar al contenido

¿Redirigir stdout a un archivo en Python?

Ten en cuenta que en las ciencias informáticas cualquier problema suele tener diferentes soluciones, así que enseñaremos lo más óptimo y eficiente.

Solución:

Si desea realizar la redirección dentro del script de Python, configure sys.stdout a un objeto de archivo hace el truco:

import sys
sys.stdout = open('file', 'w')
print('test')
sys.stdout.close()

Un método mucho más común es usar la redirección de shell al ejecutar (lo mismo en Windows y Linux):

$ python foo.py > file

Hay contextlib.redirect_stdout() función en 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`')

Esto es similar a:

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

que se puede usar en versiones anteriores de Python. La última versión no es reutilizable. Se puede hacer uno si se desea.

No redirige la salida estándar en el nivel de descriptores de archivos, por ejemplo:

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' y 'echo this also is not redirected' no son redirigidos a la output.txt expediente.

Para redirigir al nivel del descriptor de archivo, os.dup2() puede ser usado:

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

El mismo ejemplo funciona ahora si stdout_redirected() se utiliza en lugar de 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 nown')
    os.system('echo this is also redirected')
print('this is goes back to stdout')

La salida que anteriormente se imprimía en stdout ahora va a output.txt siempre y cuando stdout_redirected() el administrador de contexto está activo.

Nota: stdout.flush() no vacía los búferes C stdio en Python 3 donde la E/S se implementa directamente en read()/write() llamadas del sistema. Para vaciar todos los flujos de salida abiertos de C stdio, puede llamar libc.fflush(None) explícitamente si alguna extensión de C usa E/S basadas en stdio:

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

podrías usar stdout parámetro para redirigir otros flujos, no solo sys.stdout ej., fusionar sys.stderr y sys.stdout:

def merged_stderr_stdout():  # $ exec 2>&1
    return stdout_redirected(to=sys.stdout, stdout=sys.stderr)

Ejemplo:

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)

Nota: stdout_redirected() mezcla E/S almacenada en búfer (sys.stdout normalmente) y E/S sin búfer (operaciones en descriptores de archivos directamente). Cuidado, podría haber problemas de almacenamiento en búfer.

Para responder, su edición: podría usar python-daemon para demonizar su script y usar logging módulo (como sugirió @ erikb85) en lugar de print declaraciones y simplemente redirigir stdout para su secuencia de comandos de Python de ejecución prolongada que ejecuta usando nohup ahora.

puedes probar esto mucho mejor

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

Recuerda algo, que tienes la capacidad de decir si te fue útil.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *