español - sys python example
¿Redirigir stdout a un archivo en Python? (10)
¿Cómo redirecciono stdout a un archivo arbitrario en Python?
Cuando se inicia un script de Python de larga duración (por ejemplo, una aplicación web) desde la sesión ssh y está en segundo plano, y la sesión ssh se cierra, la aplicación generará IOError y fallará en el momento en que intenta escribir en stdout. Necesitaba encontrar una manera de hacer que la aplicación y los módulos se envíen a un archivo en lugar de a la salida estándar para evitar fallas debido a IOError. Actualmente, empleo 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 probé sys.stdout = open(''somefile'', ''w'')
, pero esto no impide que algunos módulos externos sigan enviando a la terminal (o tal vez la línea sys.stdout = ...
no se disparó en todos). Sé que debería funcionar con scripts más simples que he probado, pero aún no tengo tiempo para probar en una aplicación web todavía.
Aquí hay una variación de la respuesta de Yuda Prawira :
- Implementar
flush()
y todos los atributos del archivo. - Escríbelo como un administrador de contexto
- captura
stderr
también
.
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())
Basado en esta respuesta: https://.com/a/5916874/1060344 , aquí hay otra forma en la que descubrí cuál uso en uno de mis proyectos. Para lo que sea que reemplace sys.stderr
o sys.stdout
, debe asegurarse de que el reemplazo cumpla con file
interfaz del file
, especialmente si es algo que está haciendo porque stderr / stdout se usa en alguna otra biblioteca que no está bajo su control . Esa biblioteca puede estar usando otros métodos de archivo objeto.
Echa un vistazo de esta manera, donde todavía dejo que todo siga haciendo stderr / stdout (o cualquier otro archivo) y también envío el mensaje a un archivo de registro utilizando la función de registro de Python (pero realmente puedes hacer cualquier cosa con esto):
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)
Citado de PEP 343 - La declaración "con" (declaración de importación agregada):
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
Utilizado de la siguiente manera:
with open(filename, "w") as f:
with stdout_redirected(f):
print "Hello world"
Esto no es seguro para subprocesos, por supuesto, pero ninguno de los dos está haciendo este mismo baile manualmente. En programas de un solo hilo (por ejemplo, en scripts) es una forma popular de hacer las cosas.
Hay contextlib.redirect_stdout()
función contextlib.redirect_stdout()
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 utilizar en versiones anteriores de Python. La última versión no es reusable . Se puede hacer uno si se desea.
No redirige la salida estándar al 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 al archivo output.txt
.
Para redirigir al nivel del descriptor de archivo, se podría usar 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
El mismo ejemplo funciona ahora si se stdout_redirected()
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'')
La salida que previamente se imprimió en stdout ahora va a output.txt
siempre que el stdout_redirected()
contexto stdout_redirected()
esté activo.
Nota: stdout.flush()
no vacia los buffers de C stdio en Python 3 donde I / O se implementa directamente en las llamadas al sistema de read()
/ write()
. Para vaciar todos los flujos de salida abiertos de C stdio, puede llamar a libc.fflush(None)
explícitamente si alguna extensión de C usa E / S basada 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ía usar el parámetro stdout
para redirigir otras secuencias, no solo sys.stdout
, por ejemplo, para 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 en sys.stdout
( sys.stdout
generalmente) y E / O sin búfer (operaciones en descriptores de archivo directamente). Cuidado, podría haber issues buffering .
Para responder, edite: podría usar python-daemon
para demonizar su script y usar el módulo de logging
(como sugirió @ erikb85 ) en lugar de declaraciones de print
y simplemente redirigir stdout para su script de Python de larga ejecución que ejecuta ahora con nohup
.
Las otras respuestas no cubrieron el caso en el que desea que los procesos bifurcados compartan su nueva salida estándar.
Para hacer eso:
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
Los programas escritos en otros idiomas (por ejemplo, C) tienen que hacer magia especial (llamada doble bifurcación) expresamente para desvincularse del terminal (y para evitar procesos zombie). Entonces, creo que la mejor solución es emularlos.
Una ventaja adicional de volver a ejecutar su programa es que puede elegir redirecciones en la línea de comandos, por ejemplo, /usr/bin/python mycoolscript.py 2>&1 1>/dev/null
Consulte esta publicación para obtener más información: ¿Cuál es la razón para realizar una bifurcación doble al crear un daemon?
Necesita un multiplexor de terminal como tmux o GNU screen
Me sorprende que un pequeño comentario de Ryan Amos a la pregunta original sea la única mención de una solución muy preferible a todas las otras que se ofrecen, sin importar qué tan inteligentes puedan ser los engaños de los pitones y la cantidad de upvotes que hayan recibido. Además del comentario de Ryan, tmux es una buena alternativa a la pantalla GNU.
Pero el principio es el mismo: si alguna vez desea dejar un trabajo en la terminal mientras se desconecta, vaya a la cafetería a tomar un sándwich, vaya al baño, vaya a su casa (etc.) y luego vuelva a conectarse a su Sesión de terminal desde cualquier lugar o cualquier computadora como si nunca hubiera estado fuera, los multiplexores de terminal son la respuesta. Piense en ellos como VNC o escritorio remoto para las sesiones de terminal. Cualquier otra cosa es una solución. Como beneficio adicional, cuando el jefe y / o el socio entran y, sin darse cuenta, presionas la ventana de tu terminal en lugar de la ventana del navegador con su contenido poco fiable, no habrás perdido las últimas 18 horas de procesamiento. !
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
Si desea hacer la redirección dentro del script de Python, establecer sys.stdout
en un objeto de archivo hace el truco:
import sys
sys.stdout = open(''file'', ''w'')
print ''test''
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
import sys
sys.stdout = open(''stdout.txt'', ''w'')