Comment reproduire tee comportement en Python lors de l'utilisation de sous-processus?
Je suis à la recherche d'un Python solution qui me permettra d'enregistrer la sortie d'une commande dans un fichier sans le cacher à partir de la console.
FYI: je veux parler d' tee (comme Unix utilitaire de ligne de commande) et non pas la fonction avec le même nom de Python intertools module.
Détails
- Python solution (pas d'appel
tee
, il n'est pas disponible sous Windows) - Je n'ai pas besoin de fournir d'informations sur stdin pour les appelés du processus de
- Je n'ai pas de contrôle sur le programme. Tout ce que je sais, c'est qu'il va afficher quelque chose sur la sortie standard stdout et stderr et de retour avec un code de sortie.
- De travail lors de l'appel de programmes externes (sous-processus)
- De travail pour les deux
stderr
etstdout
- Être capable de différencier entre stdout et stderr parce que je peut demander l'affichage d'un seul de la à la console ou je pourrais essayer de la sortie stderr à l'aide d'une couleur différente, ce qui signifie que
stderr = subprocess.STDOUT
ne fonctionnera pas. - Vivre de sortie (progressif) - le processus peut s'exécuter pendant une longue période, et je ne suis pas en mesure d'attendre qu'elle se termine.
- Python 3 du code compatible (important)
Références
Voici quelques incomplète des solutions que j'ai trouvé jusqu'à présent:
- http://devlishgenius.blogspot.com/2008/10/logging-in-real-time-in-python.html (mkfifo ne fonctionne que sur les systèmes Unix)
- http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html (ne fonctionne pas du tout)
Diagramme http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
Code actuel (deuxième essai)
#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
La production réelle
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
Résultat attendu est que les lignes commandées. Remarque, la modification de la Popen à l'utilisation d'un seul TUYAU n'est pas autorisé parce que dans la vraie vie je veux faire des choses différentes avec stderr et stdout.
Également la même dans le second cas, je n'ai pas été en mesure d'obtenir en temps réel comme, en fait, tous les résultats ont été reçus lorsque le processus est terminé. Par défaut, Popen devrait utilisez pas de tampons (bufsize=0).
- connexes: Python sous-processus les enfants à la sortie vers un fichier et le terminal?
- connexes: sous-processus.Popen: clonage stdout et stderr à la fois à la borne et des variables
- Double Possible de Python Popen: Écrire sur la sortie standard stdout ET le fichier journal simultanément le Vote de cette façon parce que c'est un wiki de la communauté 🙂
Vous devez vous connecter pour publier un commentaire.
Je vois que c'est un vieux post, mais juste au cas où quelqu'un est toujours à la recherche d'un moyen de le faire:
stdout, stderr = proc.communicate()
plus facile à utiliser.readline()
avecreadline(size)
. J'ai fait quelque chose de similaire dans d'autres langues. Ref: docs.python.org/3/library/io.html#io.TextIOBase.readlinereadline(size)
ne fixera pas l'impasse. stdout/stderr devrait être lu en même temps. Voir les liens en vertu de la question qui montrent des solutions à l'aide de threads ou asyncio.stdout
? Disonsproc
produit deux dernières lignes sur la sortie standard dans le délai de deuxproc.poll()
appels: 1.proc.poll() == None
-> lire une seule ligne -> une ligne existe dansstdout
mais le processus est terminé -> 2.proc.poll() == returncode
et lawhile
boucle des pauses (alors qu'il est encore lignes restantes dansstdout
). Aussi, pensez à définirstderr
àsubprocess.STDOUT
pour éviter les blocages.C'est un simple port de
té
de Python.Je suis en cours d'exécution sur Linux pour l'instant, mais ça devrait fonctionner sur la plupart des plates-formes.
Maintenant, pour la
subprocess
partie, je ne sais pas comment vous voulez "fil de fer" le sous-processus destdin
,stdout
etstderr
à votrestdin
,stdout
,stderr
et fichier lavabos, mais je sais que vous pouvez le faire:Vous pouvez maintenant accéder à
destinataire de l'appel.stdin
,destinataire de l'appel.stdout
etdestinataire de l'appel.stderr
comme des fichiers normaux, permettant au-dessus de "solution" à travailler. Si vous voulez obtenir ledestinataire de l'appel.code_retour
, vous aurez besoin de faire un appel supplémentaire àdestinataire de l'appel.poll()
.Être prudent avec l'écriture de
callee.stdin
: si le processus s'est terminé quand vous faites cela, une erreur peut être monté (sur Linux, je reçoisIOError: [Errno 32] Broken pipe
).tee(f_in, f_out, len, flags)
API, mais ce n'est pas le point de droit?stdout
, on lit surstderr
. Si vous allez écrire à la fois pour le même fichier, vous pouvez acquérir un verrou sur le puits lorsque vous démarrez la lecture et de le libérer après avoir écrit un terminateur de ligne. :/line1 line3 line5 line7 line9
sur stderr,line0 line2 line4 line6 line8
sur la sortie standard stdout. Bien sûr, dans cette course lastderr
thread qui s'est passé pour obtenir la sortie de la première, ce qui signifiait que vous aviezline1 line0 line3 line2 line5 line4...
au lieu deline0 line1 line2 line3 line4 line5...
-- mais vous n'avez pasline0 line3 line5 line1 line2...
ouline4 line2 line1 line0 line6...
ouline0 liline1 line3 linne2 line3e5...
. J'ai peur que pour un programme qui a pour accepter l'arbitraire d'entrée de ce genre de non-déterminisme est unaivoidable si ce n'est même pas nécessaire.Si vous ne voulez pas interagir avec le processus, vous pouvez utiliser le sous-processus module de l'amende juste.
Exemple:
tester.py
testing.py
Dans votre situation, vous pouvez simplement écrire stdout/stderr dans un fichier en premier. Vous pouvez envoyer des arguments à vos processus à communiquer, même si je n'étais pas en mesure de comprendre comment continuellement interagir avec le processus secondaire.
p.poll()
de les récupérer.C'est la façon dont il peut être fait
print()
au lieu desys.stdout.write()
. 🙂Si elle a besoin de python 3.6 n'est pas un problème il y a maintenant un moyen de le faire à l'aide de asyncio. Cette méthode vous permet de capturer des stdout et stderr séparément, mais encore les deux flux à l'ats sans l'aide de threads. Voici une ébauche:
Le code ci-dessus est basé sur ce blog: https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/
Essayez ceci :
closed
.subprocess.Popen
appelsfileno()
, le déclenchement d'une exception.Ma solution n'est pas élégant, mais il fonctionne.
Vous pouvez utiliser powershell pour accéder à "tee" en vertu de WinOS.
ping
dans MacOS.