Mon script python utilise un sous-processus pour appeler un utilitaire Linux très bruyant. Je veux stocker toute la sortie dans un fichier journal et en montrer une partie à l'utilisateur. Je pensais que ce qui suit fonctionnerait, mais la sortie n'apparaît dans mon application que lorsque l'utilitaire a produit une quantité importante de sortie.
#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
print hex(i)*512
i += 1
time.sleep(0.5)
#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
#the real code does filtering here
print "test:", line.rstrip()
Le comportement que je veux vraiment est que le script de filtre imprime chaque ligne telle qu'elle est reçue du sous-processus. Sorta comme quoi, tee
mais avec du code python.
Qu'est-ce que je rate? Est-ce seulement possible?
Mettre à jour:
Si a sys.stdout.flush()
est ajouté à fake_utility.py, le code a le comportement souhaité en python 3.1. J'utilise python 2.6. On pourrait penser que l'utilisation proc.stdout.xreadlines()
fonctionnerait de la même manière que py3k, mais ce n'est pas le cas.
Mise à jour 2:
Voici le code de travail minimal.
#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
print i
sys.stdout.flush()
time.sleep(0.5)
#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
print line.rstrip()
la source
print line,
place deprint line.rstrip()
(note: virgule à la fin).subprocess.communicate()
Réponses:
Cela fait longtemps que je n'ai pas travaillé avec Python pour la dernière fois, mais je pense que le problème vient de l'instruction
for line in proc.stdout
, qui lit l'intégralité de l'entrée avant de l'itérer. La solution est d'utiliser à lareadline()
place:Bien sûr, vous devez toujours gérer la mise en mémoire tampon du sous-processus.
Remarque: selon la documentation, la solution avec un itérateur devrait être équivalente à l'utilisation
readline()
, à l'exception du tampon de lecture anticipée, mais (ou exactement à cause de cela) la modification proposée a produit des résultats différents pour moi (Python 2.5 sur Windows XP).la source
file.readline()
vsfor line in file
voir bugs.python.org/issue3907 (en bref: cela fonctionne sur Python3; utiliserio.open()
sur Python 2.6+)for line in iter(proc.stdout.readline, ''):
.for line in proc.stdout
sur Python 3 (il n'y a pas de bogue de lecture anticipée) 2.'' != b''
sur Python 3 - ne copiez-collez pas le code à l'aveugle - pensez à ce qu'il fait et comment il fonctionne.iter(f.readline, b'')
solution est plutôt évidente (et fonctionne également sur Python 2, si quelqu'un est intéressé). Le but de mon commentaire n'était pas de blâmer votre solution (désolé si elle apparaissait comme ça, je l'ai lu maintenant aussi!), Mais de décrire l'étendue des symptômes, qui sont assez sévères dans ce cas (la plupart des Py2 / 3 problèmes entraînent des exceptions, alors qu'ici une boucle bien tenue a changé pour être sans fin, et la collecte des ordures a du mal à lutter contre le flot d'objets nouvellement créés, produisant des oscillations d'utilisation de la mémoire avec une longue période et une grande amplitude).Un peu tard pour la fête, mais j'ai été surpris de ne pas voir ce que je pense être la solution la plus simple ici:
(Cela nécessite Python 3.)
la source
AttributeError: 'file' object has no attribute 'readable'
py2.7TextIOWrapper
ou non. Vous pouvez simplement gérer l'exception.En effet, si vous triez l'itérateur, la mise en mémoire tampon peut maintenant être votre problème. Vous pouvez dire au python du sous-processus de ne pas mettre sa sortie en mémoire tampon.
devient
J'en avais besoin lors de l'appel de python depuis python.
la source
Vous souhaitez transmettre ces paramètres supplémentaires à
subprocess.Popen
:Ensuite, vous pouvez répéter comme dans votre exemple. (Testé avec Python 3.5)
la source
Une fonction qui permet d'itérer sur les deux
stdout
etstderr
simultanément, en temps réel, ligne par ligneSi vous avez besoin d'obtenir le flux de sortie pour les deux
stdout
etstderr
en même temps, vous pouvez utiliser la fonction suivante.La fonction utilise des files d'attente pour fusionner les deux canaux Popen en un seul itérateur.
Ici, nous créons la fonction
read_popen_pipes()
:read_popen_pipes()
utilisé:la source
Vous pouvez également lire des lignes sans boucle. Fonctionne en python3.6.
la source
list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
J'ai essayé cela avec python3 et cela a fonctionné, source
la source
La modification suivante de la réponse de Rômulo fonctionne pour moi sur Python 2 et 3 (2.7.12 et 3.6.1):
la source
Ne sais pas quand cela a été ajouté au module de sous-processus, mais avec Python 3, vous devriez bien utiliser
proc.stdout.splitlines()
:la source