Comment rediriger stdout vers un fichier arbitraire en Python?
Lorsqu'un script Python de longue durée (par exemple, une application Web) est démarré à partir de la session ssh et rétrogradé, et que la session ssh est fermée, l'application déclenche IOError et échoue au moment où elle essaie d'écrire sur stdout. J'avais besoin de trouver un moyen de faire sortir l'application et les modules dans un fichier plutôt que stdout pour éviter les échecs dus à IOError. Actuellement, j'utilise nohup pour rediriger la sortie vers un fichier, et cela fait le travail, mais je me demandais s'il y avait un moyen de le faire sans utiliser nohup, par curiosité.
J'ai déjà essayé sys.stdout = open('somefile', 'w')
, mais cela ne semble pas empêcher certains modules externes de continuer à sortir vers le terminal (ou peut-être que la sys.stdout = ...
ligne ne s'est pas déclenchée du tout). Je sais que cela devrait fonctionner à partir de scripts plus simples sur lesquels j'ai testé, mais je n'ai pas encore eu le temps de tester sur une application Web.
script.p > file
someprocess | python script.py
? Pourquoi impliquernohup
?print
instructions pour appliquer lelogging
module à partir de stdlib. Ensuite, vous pouvez rediriger la sortie partout, contrôler la quantité de sortie souhaitée, etc. Dans la plupart des cas, le code de production ne devrait pasprint
maislog
.Réponses:
Si vous souhaitez effectuer la redirection dans le script Python, la définition
sys.stdout
d'un objet fichier fait l'affaire:Une méthode beaucoup plus courante consiste à utiliser la redirection de shell lors de l'exécution (idem sous Windows et Linux):
la source
from sys import stdout
, peut-être parce qu'il crée une copie locale. Vous pouvez également l'utiliser avecwith
, par exemplewith open('file', 'w') as sys.stdout: functionThatPrints()
. Vous pouvez maintenant implémenter enfunctionThatPrints()
utilisant desprint
instructions normales .stdout = sys.stdout
afin que vous puissiez le remettre lorsque vous avez terminé,sys.stdout = stdout
. De cette façon, si vous êtes appelé à partir d'une fonction qui l'utiliseprint
, ne les gâchez pas.buffering=0
désactive la mise en mémoire tampon (cela peut affecter négativement les performances (10 à 100 fois)).buffering=1
active la mise en mémoire tampon des lignes afin que vous puissiez l'utilisertail -f
pour une sortie orientée ligne.sys.stdout = sys.__stdout__
pour le récupérer.Il y a une
contextlib.redirect_stdout()
fonction dans Python 3.4:C'est similaire à:
qui peut être utilisé sur les versions antérieures de Python. Cette dernière version n'est pas réutilisable . Il peut être fait si vous le souhaitez.
Il ne redirige pas la sortie standard au niveau des descripteurs de fichiers, par exemple:
b'not redirected'
et'echo this also is not redirected'
ne sont pas redirigés vers leoutput.txt
fichier.Pour rediriger au niveau du descripteur de fichier,
os.dup2()
pourrait être utilisé:Le même exemple fonctionne maintenant si
stdout_redirected()
est utilisé à la place deredirect_stdout()
:La sortie qui était précédemment imprimée sur stdout va maintenant
output.txt
tant que lestdout_redirected()
gestionnaire de contexte est actif.Remarque:
stdout.flush()
ne vide pas les tampons stdio C sur Python 3 où les E / S sont implémentées directement surread()
/ leswrite()
appels système. Pour vider tous les flux de sortie stdio C ouverts, vous pouvez appelerlibc.fflush(None)
explicitement si une extension C utilise des E / S basées sur stdio:Vous pouvez utiliser un
stdout
paramètre pour rediriger d'autres flux, non seulementsys.stdout
par exemple, pour fusionnersys.stderr
etsys.stdout
:Exemple:
Remarque:
stdout_redirected()
mélange les E / S tamponnées (sys.stdout
généralement) et les E / S non tamponnées (opérations sur les descripteurs de fichiers directement). Attention, il pourrait y avoir des problèmes de mise en mémoire tampon .Pour répondre, votre modification: vous pouvez utiliser
python-daemon
pour démonifier votre script et utiliser lelogging
module (comme @ erikb85 l'a suggéré ) au lieu d'print
instructions et simplement rediriger stdout pour votre script Python de longue durée que vous exécuteznohup
maintenant.la source
stdout_redirected
est utile. Sachez que cela ne fonctionne pas dans les doctests, car leSpoofOut
gestionnaire spécial que doctest utilise pour remplacersys.stdout
n'a pas d'fileno
attribut.ValueError("Expected a file (`.fileno()`) or a file descriptor")
c'est un bug. Êtes-vous sûr que cela ne le soulève pas?doctest.sys.__stdout__
où nous utiliserions normalementsys.stdout
. Ce n'est pas un problème avec votre fonction, juste un accommodement requis pour doctest car il remplace stdout par un objet qui n'a pas tous les attributs d'un vrai fichier.stdout_redirected()
a unstdout
paramètre, vous pouvez le définirsys.__stdout__
si vous souhaitez rediriger la sortie standard de python (qui devrait avoir une valeur valide.fileno()
dans la plupart des cas). Cela ne fait rien pour le courantsys.stdout
s'ils sont différents. Ne pas utiliserdoctest.sys
; il est disponible par accident.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
vous pouvez essayer cela beaucoup mieux
la source
logger
ousyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
méthode à la classeLogger
.Les autres réponses ne couvraient pas le cas où vous souhaitez que les processus fourchus partagent votre nouvelle sortie standard.
Pour faire ça:
la source
sys.stdout.flush()
avant l'close(1)
instruction pour vous assurer que le'file'
fichier de redirection obtient la sortie. Vous pouvez également utiliser untempfile.mkstemp()
fichier à la place de'file'
. Et soyez prudent, vous n'avez pas d'autres threads en cours d'exécution qui peuvent voler le premier handle de fichier du système d'exploitation après leos.close(1)
mais avant que le'file'
soit ouvert pour utiliser le handle.os.dup2()
et l'envelopper dans un gestionnaire de contexte, comme indiqué dans ma réponseCité du PEP 343 - La déclaration "with" (déclaration d'importation ajoutée):
Rediriger temporairement la sortie standard:
Utilisé comme suit:
Ce n'est pas thread-safe, bien sûr, mais ne fait pas cette même danse manuellement. Dans les programmes à thread unique (par exemple dans les scripts), c'est une façon populaire de faire les choses.
la source
os.system('echo not redirected')
. Ma réponse montre comment rediriger une telle sortieredirect_stdout
encontextlib
la source
Voici une variante de la réponse de Yuda Prawira :
flush()
et tous les attributs de fichierstderr
aussi.
la source
Sur la base de cette réponse: https://stackoverflow.com/a/5916874/1060344 , voici une autre façon dont j'ai compris que j'utilise dans l'un de mes projets. Pour tout ce que vous remplacez
sys.stderr
ousys.stdout
avec, vous devez vous assurer que le remplacement est compatible avec l'file
interface, surtout si c'est quelque chose que vous faites parce que stderr / stdout sont utilisés dans une autre bibliothèque qui n'est pas sous votre contrôle. Cette bibliothèque peut utiliser d'autres méthodes d'objet fichier.Découvrez de cette façon où je laisse toujours tout faire stderr / stdout (ou n'importe quel fichier d'ailleurs) et envoie également le message à un fichier journal à l'aide de la fonction de journalisation de Python (mais vous pouvez vraiment faire quoi que ce soit avec cela):
la source
Vous avez besoin d'un multiplexeur de terminal comme tmux ou écran GNU
Je suis surpris qu'un petit commentaire de Ryan Amos à la question d'origine soit la seule mention d'une solution de loin préférable à toutes les autres proposées, peu importe à quel point la supercherie en python peut être intelligente et combien de votes positifs ils ont reçus. Suite au commentaire de Ryan, tmux est une belle alternative à l'écran GNU.
Mais le principe est le même: si jamais vous vous retrouvez à vouloir quitter un travail terminal pendant que vous vous déconnectez, rendez-vous au café pour un sandwich, passez aux toilettes, rentrez chez vous (etc.), puis plus tard, reconnectez-vous à votre session de terminal depuis n'importe où ou depuis n'importe quel ordinateur comme si vous n'aviez jamais été absent, les multiplexeurs de terminaux sont la réponse. Considérez-les comme VNC ou bureau à distance pour les sessions de terminal. Tout le reste est une solution de contournement. En bonus, lorsque le patron et / ou le partenaire arrive et que vous ctrl-w / cmd-w par inadvertance votre fenêtre de terminal au lieu de la fenêtre de votre navigateur avec son contenu douteux, vous n'aurez pas perdu les 18 dernières heures de traitement !
la source
Les programmes écrits dans d'autres langages (par exemple C) doivent faire une magie spéciale (appelée double forking) pour se détacher expressément du terminal (et pour empêcher les processus zombies). Donc, je pense que la meilleure solution est de les émuler.
Un plus de la réexécution de votre programme est que vous pouvez choisir des redirections sur la ligne de commande, par exemple
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
Voir cet article pour plus d'informations: Quelle est la raison d'effectuer un double fork lors de la création d'un démon?
la source
systemd
,upstart
) ou un autre utilitaire (daemon(1)
) pour gérer le passe-partout de la fourche.