321 votos

Redirigir stdout a un archivo en Python?

¿Cómo puedo redirigir stdout a un archivo arbitrario en Python?

EDITAR en el comentario del pedido:

Cuando una larga secuencia de comandos de Python (e.g, la aplicación web) se inicia desde dentro de la sesión de ssh y backgounded, y en la sesión de ssh está cerrado, la aplicación elevará IOError y fallan en el momento en que se intenta escribir en la salida estándar. Necesitaba encontrar una manera de hacer que la aplicación y los módulos de salida a un archivo en lugar de stdout para evitar que el fallo debido a IOError. En la actualidad, que emplean nohup para redirigir la salida a un archivo, y eso hace el trabajo, pero me preguntaba si había una manera de hacerlo sin usar nohup, por curiosidad.

Ya he probado sys.stdout = open('somefile', 'w'), pero esto no parece a prevenir algunos de los módulos externos de que aún la salida a terminal (o tal vez la sys.stdout = ... línea no funcionó en absoluto). Sé que se debe trabajar más simples scripts que he probado, pero tampoco tuve tiempo de probar en un sitio web la solicitud de embargo.

410voto

marcog Puntos 39356

Si usted quiere hacer la redirección dentro de la secuencia de comandos de Python, ajuste sys.stdout a un objeto de archivo hace el truco:

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

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

 $ python foo.py > file
 

186voto

J.F. Sebastian Puntos 102961

Hay contextlib.redirect_stdout() de 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`')

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 puede ser utilizado en anteriores versiones de Python. La última versión no es reutilizable. Se puede hacer uno si lo desea.

No redirigir la salida estándar (stdout) en el archivo de los descriptores de nivel, 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 se le redirige a la output.txt archivo.

Para redirigir en el archivo descriptor de nivel, os.dup2() podría ser utilizado:

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 now\n')
    os.system('echo this is also redirected')
print('this is goes back to stdout')

El resultado de lo que previamente se imprime en la salida estándar (stdout) pasa ahora a output.txt mientras stdout_redirected() contexto gerente está activo.

Usted podría utilizar stdout parámetro para redirigir a otras corrientes, no sólo sys.stdout por ejemplo, para combinar 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() mezclas de búfer de e/S (sys.stdout por lo general) y sin búfer de e/S (de las operaciones sobre los descriptores de archivo directamente). Ten cuidado, podría ser el búfer de problemas.

Para responder, la edición: se puede usar python-daemon a daemonize su guión y el uso logging módulo (como @erikb85 sugerido) en lugar de print declaraciones y se limitó a redirigir stdout para su ejecución larga secuencia de comandos de Python que se ejecuta utilizando nohup ahora.

92voto

Gunslinger_ Puntos 2293

puedes probar esto también 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
 

28voto

Yam Marcovic Puntos 1566

Las otras respuestas no cubren el caso en el que desea procesos bifurcados para compartir su nueva salida estándar.

Para ello:

 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
 

28voto

Gerli Puntos 161

Citado de PEP 343 - El "con" Declaración (sentencia de importación adicional):

Redirigir stdout temporalmente:

 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
 

Se utiliza de la siguiente manera:

 with opened(filename, "w") as f:
    with stdout_redirected(f):
        print "Hello world"
 

Esto no es seguro para subprocesos, por supuesto, pero tampoco está haciendo esta misma danza manualmente. En los programas de un único subproceso (por ejemplo, en las secuencias de comandos) es una forma popular de hacer las cosas.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X