¿Cómo redirijo 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 retrocede, y la sesión ssh se cierra, la aplicación generará IOError y fallará en el momento en que intente escribir en stdout. Necesitaba encontrar una manera de hacer que la aplicación y los módulos salieran a un archivo en lugar de stdout para evitar fallas debido a IOError. Actualmente, uso 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 lo he intentado sys.stdout = open('somefile', 'w')
, pero esto no parece evitar que algunos módulos externos sigan saliendo al terminal (o tal vez la sys.stdout = ...
línea no se disparó en absoluto). Sé que debería funcionar a partir de scripts más simples que he probado, pero todavía no tuve tiempo para probar en una aplicación web todavía.
script.p > file
someprocess | python script.py
? ¿Por qué involucrarsenohup
?print
declaraciones para aplicar ellogging
módulo desde stdlib. A continuación, puede redirigir la salida de todo el mundo, tener control sobre la cantidad de salida que desea etc. En la mayoría de casos el código de producción no deberíaprint
perolog
.Respuestas:
Si desea hacer la redirección dentro de la secuencia de comandos de Python, la configuración
sys.stdout
de un objeto de archivo hace el truco:Un método mucho más común es utilizar la redirección de shell al ejecutar (lo mismo en Windows y Linux):
fuente
from sys import stdout
, quizás porque crea una copia local. También puedes usarlo conwith
, por ejemplowith open('file', 'w') as sys.stdout: functionThatPrints()
. Ahora puede implementarfunctionThatPrints()
usandoprint
declaraciones normales .stdout = sys.stdout
para que pueda volver a guardarla cuando haya terminadosys.stdout = stdout
. De esa manera, si te llaman desde una función que usaprint
, no los arruinas.buffering=0
deshabilita el almacenamiento en búfer (puede afectar negativamente el rendimiento (10-100 veces)).buffering=1
habilita el almacenamiento en línea para que pueda usarlotail -f
para una salida orientada a línea.sys.stdout = sys.__stdout__
para recuperarlo.Hay una
contextlib.redirect_stdout()
función en Python 3.4:Esto es similar a:
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 el stdout en el nivel de descriptores de archivo, por ejemplo:
b'not redirected'
y'echo this also is not redirected'
no se redirigen aloutput.txt
archivo.Para redirigir en el nivel de descriptor de archivo,
os.dup2()
podría usarse:El mismo ejemplo funciona ahora si
stdout_redirected()
se usa en lugar deredirect_stdout()
:El resultado que antes se imprimía en stdout ahora se mantiene
output.txt
mientras elstdout_redirected()
administrador de contexto esté activo.Nota:
stdout.flush()
no vacía las memorias intermedias de C stdio en Python 3 donde la E / S se implementa directamente en las llamadasread()
/write()
system. Para vaciar todas las secuencias de salida de C stdio abiertas, puede llamarlibc.fflush(None)
explícitamente si alguna extensión de C usa E / S basadas en stdio:Puede usar el
stdout
parámetro para redirigir otras secuencias, no solosys.stdout
, por ejemplo, para fusionarsys.stderr
ysys.stdout
:Ejemplo:
Nota:
stdout_redirected()
mezcla E / S con búfer (sys.stdout
generalmente) y E / S sin búfer (operaciones en descriptores de archivo directamente). Cuidado, podría haber problemas de almacenamiento en búfer .Para responder, su edición: puede usar
python-daemon
para demonizar su script y usar ellogging
módulo (como sugirió @ erikb85 ) en lugar deprint
declaraciones y simplemente redireccionar stdout para su script Python de larga ejecución que ejecutanohup
ahora.fuente
stdout_redirected
es útil. Tenga en cuenta que esto no funciona dentro de doctests, ya que elSpoofOut
controlador especial que doctest utiliza para reemplazarsys.stdout
no tiene unfileno
atributo.ValueError("Expected a file (`.fileno()`) or a file descriptor")
entonces es un error. ¿Estás seguro de que no lo eleva?doctest.sys.__stdout__
dónde usaríamos normalmentesys.stdout
. Esto no es un problema con su función, solo un ajuste requerido para doctest ya que reemplaza stdout con un objeto que no tiene todos los atributos que tendría un archivo verdadero.stdout_redirected()
tiene unstdout
parámetro, puede configurarlosys.__stdout__
si desea redirigir la stdout original de Python (que debería tener una validez.fileno()
en la mayoría de los casos). No hace nada por la corrientesys.stdout
si son diferentes. No utilizardoctest.sys
; Está disponible por accidente.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
puedes probar esto mucho mejor
fuente
logger
osyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
método también a la claseLogger
.Las otras respuestas no cubrieron el caso en el que desea que los procesos bifurcados compartan su nuevo stdout.
Para hacer eso:
fuente
sys.stdout.flush()
antes de laclose(1)
declaración para asegurarse de que el'file'
archivo de redireccionamiento obtenga el resultado. Además, puede usar untempfile.mkstemp()
archivo en lugar de'file'
. Y tenga cuidado de no tener otros subprocesos en ejecución que puedan robar el primer identificador de archivo del sistema operativo después delos.close(1)
pero antes de'file'
abrirlo para usar el identificador.os.dup2()
y envolverlo en un administrador de contexto como se muestra en mi respuestaCitado de PEP 343 - La declaración "con" (declaración de importación agregada):
Redireccionar stdout temporalmente:
Usado de la siguiente manera:
Esto no es seguro para subprocesos, por supuesto, pero tampoco lo está haciendo este mismo baile manualmente. En los programas de subproceso único (por ejemplo, en los scripts) es una forma popular de hacer las cosas.
fuente
os.system('echo not redirected')
. Mi respuesta muestra cómo redirigir dicha salidaredirect_stdout
encontextlib
fuente
Aquí hay una variación de la respuesta de Yuda Prawira :
flush()
y todos los atributos del archivostderr
también.
fuente
En base a esta respuesta: https://stackoverflow.com/a/5916874/1060344 , aquí hay otra forma que descubrí que utilizo en uno de mis proyectos. Para lo que sea que reemplace
sys.stderr
osys.stdout
con, debe asegurarse de que el reemplazo cumpla con lafile
interfaz, especialmente si esto es algo que está haciendo porque stderr / stdout se usan en alguna otra biblioteca que no está bajo su control. Esa biblioteca puede estar utilizando otros métodos de objeto de archivo.Eche un vistazo de esta manera donde todavía dejo que todo vaya a hacer stderr / stdout (o cualquier archivo para el caso) y también envíe el mensaje a un archivo de registro utilizando la función de registro de Python (pero realmente puede hacer cualquier cosa con esto):
fuente
Necesita un multiplexor de terminal como tmux o pantalla GNU
Me sorprende que un pequeño comentario de Ryan Amos a la pregunta original sea la única mención de una solución mucho más preferible a todas las demás que se ofrecen, sin importar cuán inteligente sea el truco de Python y cuántos votos positivos 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 te encuentras con ganas de dejar un trabajo en la terminal mientras cierras la sesión, ve al café a comer un sándwich, ve al baño, ve a casa (etc.) y luego, vuelve a conectarte con tu sesión terminal desde cualquier lugar o cualquier computadora como si nunca hubiera estado fuera, los multiplexores terminales son la respuesta. Piense en ellos como VNC o escritorio remoto para sesiones de terminal. Cualquier otra cosa es una solución alternativa. Como beneficio adicional, cuando entra el jefe y / o el socio y, sin darse cuenta, ctrl-w / cmd-w su ventana de terminal en lugar de la ventana de su navegador con su contenido dudoso, no habrá perdido las últimas 18 horas de procesamiento !
fuente
Los programas escritos en otros lenguajes (por ejemplo, C) tienen que hacer magia especial (llamada doble bifurcación) expresamente para desconectarse de la terminal (y para evitar procesos zombis). 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, p. Ej.
/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 doble bifurcación al crear un demonio?
fuente
systemd
,upstart
) u otra utilidad (daemon(1)
) para manejar la placa de horquilla.