Il existe la fonction contextlib.redirect_stdout()
en Python 3.4+:
from contextlib import redirect_stdout
with open('help.txt', 'w') as f:
with redirect_stdout(f):
print('il imprime maintenant dans `help.text`')
Il est similaire à :
import sys
from contextlib import contextmanager
@contextmanager
def redirect_stdout(new_target):
old_target, sys.stdout = sys.stdout, new_target # Remplacer sys.stdout
try:
yield new_target # Exécuter du code avec le stdout remplacé
finally:
sys.stdout = old_target # Restaurer à la valeur précédente
qui peut être utilisé sur des versions antérieures de Python. La version ultérieure n'est pas réutilisable. Elle peut être rendue réutilisable si désiré.
Cela ne redirige pas le stdout au niveau des descripteurs de fichiers par exemple :
import os
from contextlib import redirect_stdout
stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, redirect_stdout(f):
print('redirigé vers un fichier')
os.write(stdout_fd, b'n'est pas redirigé')
os.system('echo ceci non plus n'est pas redirigé')
b'n'est pas redirigé'
et 'echo ceci non plus n'est pas redirigé'
ne sont pas redirigés vers le fichier output.txt
.
Pour rediriger au niveau du descripteur de fichier, os.dup2()
peut être utilisé :
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("Un fichier (`.fileno()`) ou un descripteur de fichier était attendu")
return fd
@contextmanager
def stdout_redirected(to=os.devnull, stdout=None):
if stdout is None:
stdout = sys.stdout
stdout_fd = fileno(stdout)
# Copier stdout_fd avant qu'il soit écrasé
#NOTE: `copied` est héritable sur Windows lors de la duplication d'un flux standard
with os.fdopen(os.dup(stdout_fd), 'wb') as copied:
stdout.flush() # Vider les buffers de librairie que dup2 ne connait pas
try:
os.dup2(fileno(to), stdout_fd) # $ exec >&to
except ValueError: # nom de fichier
with open(to, 'wb') as to_file:
os.dup2(to_file.fileno(), stdout_fd) # $ exec > to
try:
yield stdout # permettre l'exécution de code avec le stdout redirigé
finally:
# restaurer stdout à sa valeur précédente
#NOTE: dup2 rend stdout_fd héritable inconditionnellement
stdout.flush()
os.dup2(copied.fileno(), stdout_fd) # $ exec >&copied
Le même exemple fonctionne maintenant si stdout_redirected()
est utilisé au lieu de redirect_stdout()
:
import os
import sys
stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, stdout_redirected(f):
print('redirigé vers un fichier')
os.write(stdout_fd, b'c'est maintenant redirigé\n')
os.system('echo ceci est aussi redirigé')
print('cela revient vers stdout')
La sortie précédemment imprimée sur stdout va maintenant dans output.txt
tant que le gestionnaire de contexte stdout_redirected()
est actif.
Note: stdout.flush()
ne vide pas les buffers C stdio sur Python 3 où l'I/O est implémentée directement sur les appels système read()
/write()
. Pour vider tous les flux de sortie C stdio ouverts, vous pourriez appeler libc.fflush(None)
explicitement si une extension C utilise une I/O basée sur 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 # non supporté
Vous pourriez utiliser le paramètre stdout
pour rediriger d'autres flux, pas seulement sys.stdout
par ex., pour fusionner sys.stderr
et sys.stdout
:
def merged_stderr_stdout(): # $ exec 2>&1
return stdout_redirected(to=sys.stdout, stdout=sys.stderr)
Exemple :
from __future__ import print_function
import sys
with merged_stderr_stdout():
print('ceci est imprimé sur stdout')
print('ceci est aussi imprimé sur stdout', file=sys.stderr)
Note: stdout_redirected()
mélange l'I/O bufferisée (sys.stdout
habituellement) et l'I/O non bufferisée (opérations sur les descripteurs de fichiers directement). Attention, il pourrait y avoir des problèmes de buffering là.
Pour répondre, à votre édition : vous pourriez utiliser python-daemon
pour mettre en service votre script et utiliser le module logging
(comme @erikb85 a suggéré) au lieu des déclarations print
et simplement rediriger stdout pour votre script Python long en cours d'exécution que vous exécutez maintenant en utilisant nohup
.
12 votes
Ce n'est pas vraiment une chose en python, c'est une fonction shell. Il suffit d'exécuter votre script comme
script.p > file
0 votes
Je résous actuellement le problème en utilisant nohup, mais je pensais qu'il pourrait y avoir quelque chose de plus intelligent...
1 votes
@foxbunny: pourquoi simplement
someprocess | python script.py
? Pourquoi impliquernohup
?0 votes
@S.Lott: Pourquoi someprocess | python script.py?
0 votes
@foxbunny: Mon erreur. Votre question est mal formulée. Je pensais que vous vouliez rediriger stdout de certains processus dans un programme Python. Il semble, d'après la réponse acceptée, que vous faites quelque chose de différent. J'aimerais toujours savoir comment vous utilisez
nohup
. Et pourquoi.0 votes
@S.Lott: J'ai accepté la réponse parce que je pense que ça fonctionnerait. Mais je ne l'ai pas encore implémentée. J'utilise nohup pour éviter de me soucier de stdout au cas où je lance le script depuis un terminal, le mets en arrière-plan, puis ferme le terminal comme ceci :
nohup python script.py > logfile &
Je continue d'utiliser cette technique car simplement assigner une poignée de fichier à sys.stdout n'a pas fonctionné pour certaines choses comme le serveur cherrypy. Ils écrivent toujours joyeusement sur stdout, mais dans certains cas, cela a eu des conséquences fatales.0 votes
@foxbunny : "attribuer une poignée de fichier à sys.stdout n'a pas fonctionné pour certaines choses comme le serveur cherrypy" Quoi? Cela n'a pas de sens. Cela ne devrait pas fonctionner. C'est un serveur web, pas une application de traitement de fichiers. Cette question est très confuse. Pouvez-vous mettre à jour la question pour clarifier de quoi vous parlez ?
0 votes
@S.Lott: Après quelques tests supplémentaires, j'ai corrigé quelques erreurs que j'avais dans le code et maintenant (contrairement à ce que vous affirmez), stdout est universellement redirigé vers un fichier selon les solutions dans les deux réponses ci-dessous. Si vous pensez toujours que cela ne devrait pas fonctionner, vous devriez essayer vous-même. Si vous ne comprenez toujours pas la question elle-même, malheureusement ce n'est pas mon souci. Regardez encore une fois les deux réponses fournies et vous devriez pouvoir comprendre.
6 votes
Réécrivez les déclarations
print
pour utiliser le modulelogging
de la bibliothèque standard. Ensuite, vous pourrez rediriger la sortie n'importe où, avoir le contrôle sur la quantité de sortie que vous souhaitez, etc. Dans la plupart des cas, le code de production ne devrait pas utiliserprint
mais plutôtlog
.5 votes
Peut-être une meilleure solution pour ce problème est la commande screen, qui sauvegardera votre session bash et vous permettra d'y accéder depuis différentes exécutions.
0 votes
Cas particulier de suppression de la sortie (il existe probablement une solution plus rapide en redirigeant vers
None
au lieu de/dev/null
): Rediriger stdout vers "rien" en python - Stack Overflow