L'obtention de la sortie en temps réel à l'aide de sous-processus
Je suis en train d'écrire un script pour un programme en ligne de commande (svnadmin vérifier) qui affiche une belle progression de l'indicateur pour l'opération. Cela m'oblige à être en mesure de voir chaque ligne de la sortie de l'enveloppé programme dès qu'elle est sortie.
J'ai pensé que je venais d'exécuter le programme à l'aide subprocess.Popen
, utilisez stdout=PIPE
, puis de lire chaque ligne comme il est venu dans et à agir en conséquence. Cependant, quand j'ai couru le code suivant, la sortie semble être tamponnée quelque part, le faisant apparaître dans deux segments, les lignes 1 à 332, puis 333 grâce à 439 (la dernière ligne de sortie)
from subprocess import Popen, PIPE, STDOUT
p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE,
stderr = STDOUT, shell = True)
for line in p.stdout:
print line.replace('\n', '')
Après avoir examiné la documentation sur les sous-processus un peu, j'ai découvert le bufsize
paramètre Popen
, j'ai donc essayé de réglage bufsize à 1 (tampon de chaque ligne) et 0 (pas de tampon), mais ni la valeur semblé changer la façon dont les lignes ont été livrés.
À ce stade, je commençais à saisir pour la paille, j'ai donc écrit à la suite de la sortie de la boucle:
while True:
try:
print p.stdout.next().replace('\n', '')
except StopIteration:
break
mais il a obtenu le même résultat.
Est-il possible d'obtenir en temps réel le programme de sortie d'un programme exécuté à l'aide d'un processus secondaire? Est-il une autre option en Python qui est compatible (pas exec*
)?
- Avez-vous essayé l'omission de la
sydout=PIPE
de sorte que le sous-processus écrit directement à votre console, en contournant le processus parent? - Le truc, c'est que j'ai envie de lire la sortie. Si c'est directement la sortie de la console, comment pourrais-je le faire? Aussi, je ne veux pas que l'utilisateur de voir la sortie de la enveloppé programme, juste à ma sortie.
- Alors pourquoi un "temps réel" afficher? Je ne reçois pas le cas d'utilisation.
- Ne pas utiliser le shell=True. Il needlessy appelle votre shell. Utiliser p = Popen(['svnadmin', 'vérifier', '/var/svn/repos/config'], stdout=PIPE, stderr=STDOUT) au lieu
- Fondamentalement, svnadmin vérifier imprime une ligne de sortie pour chaque révision, qui est vérifiée. Je voulais faire un bon indicateur de progrès qui ne cause pas de quantités excessives de sortie. Un peu comme wget, par exemple
- J'ai essayé en omettant shell=True lorsque je travaillais avec cela, mais il ne serait jamais exécuter sans elle. J'ai même utilisé le chemin d'accès complet à svnadmin dans le cas où le CHEMIN n'est pas défini si j'ai utilisé shell=Faux, mais ça n'a pas réglé non plus.
- Vous avez besoin de fractionner la commande en vous-même pour être en mesure d'omettre le
shell=True
mais c'est une simple modification. @nosklo commentaire vous montre comment; passer le premier argument est une liste de jetons, pas une seule chaîne.
Vous devez vous connecter pour publier un commentaire.
J'ai essayé cela, et pour quelque raison que ce soit, le code
tampons de manière agressive, la variante
ne le fait pas. Apparemment c'est un bug connu: http://bugs.python.org/issue3907 (La question est maintenant de "Fermé" à compter du 29 Août, 2018)
while p.poll() is None
au lieu dewhile True
, et de supprimer leif not line
print(line.decode('utf-8').rstrip())
.PYTHONUNBUFFERED=1
. Ceci est particulièrement utile pour les sorties qui sont infinisp.stdout.close()
est pas claire.Vous pouvez essayer ceci:
Si vous utiliser readline au lieu de lire, il y aura des cas où le message n'est pas imprimé. Essayez avec une commande requiert une ligne d'entrée et de voir par vous-même.
Vous pouvez diriger le sous-processus de sortie du flux directement. Exemple simplifié:
.communicate()
? Ou sont les contenus perdus pour le parent stderr/stdout flux?communicate()
méthode sur le revenuCompletedProcess
. Aussi,capture_output
est mutuellement exclusive avecstdout
etstderr
.La Streaming sous-processus stdin et stdout avec asyncio en Python blog par Kevin McCarthy montre comment le faire avec asyncio:
import nest_asyncio; nest_asyncio.apply()
et d'utiliser les commandes shell, c'est à direprocess = await create_subprocess_shell(*command, stdout=PIPE, stderr=PIPE, shell=True)
au lieu deprocess = await create_subprocess_exec(...)
. Cheers!J'ai rencontré le même problème pendant quelque temps en arrière. Ma solution a été d'abandonner l'itération pour la
read
méthode, qui sera de retour immédiatement, même si votre sous-processus n'est pas fini son exécution, etc.Temps réel de Sortie résolution du Problème:
Je n'ai rencontré de problème similaire en Python, lors de la capture en temps réel de la sortie d'un programme c. J'ai ajouté "fflush(stdout);" dans mon code C. Il a travaillé pour moi. Voici la capture du code
<< C >>
<< Programme en Python >>
<< PRODUIT>>
D'Impression: Nombre 1
Impression: Comptez 2
Impression: Compter 3
Espère que cela aide.
~sairam
flush(stdout)
) en C++. Merci!Selon le cas d'utilisation, vous pouvez également désactiver la mise en mémoire tampon dans le sous-processus lui-même.
Si le sous-processus sera un Python processus, vous pourriez le faire avant l'appel:
Ou sinon passer cela dans le
env
argumentPopen
.Sinon, si vous êtes sur Linux/Unix, vous pouvez utiliser le
stdbuf
outil. E. g. comme:Voir aussi ici sur
stdbuf
ou d'autres options.(Voir aussi ici pour la même réponse.)
J'ai utilisé cette solution pour obtenir la sortie en temps réel sur un sous-processus. Cette boucle s'arrêtera dès que le processus se termine en laissant un besoin pour une instruction break ou possible de boucle infinie.
if out=='': break
aprèsout = sub_process...
Vous pouvez utiliser un itérateur sur chaque octet de la sortie de la sous-processus. Cela permet inline mise à jour (les lignes se terminant par '\r' écraser la précédente ligne de sortie) de la sous-processus:
À l'aide de pexpect [ http://www.noah.org/wiki/Pexpect ] avec les non-bloquant readlines pour résoudre ce problème. Elle vient du fait que les tuyaux sont mises en mémoire tampon, et ainsi, votre application est de sortie est prise en tamponnée par le tuyau, donc vous ne pouvez pas obtenir à la sortie jusqu'à ce que le tampon se remplit ou le processus meurt.
Trouvé ce "plug-and-play"ici. A travaillé comme un charme!
stderr=subprocess.STDOUT
en fait nous aide beaucoup pour la capture de données en continu. Je suis upvoting il.En Python 3.x le processus peut se bloquer parce que la sortie est un tableau d'octets au lieu d'une chaîne. Assurez-vous de le décoder dans une chaîne.
À partir de Python 3.6 vous pouvez le faire en utilisant le paramètre
encoding
dans Popen Constructeur. L'exemple complet:Notez que ce code les redirections
stderr
àstdout
et gère les erreurs de sortie.Solution complète:
universal_newlines=True
sur lePopen()
appel, vous n'avez probablement pas besoin de mettre votre propre de manutention dans les, trop -- c'est toute la question de l'option.C'est le squelette de base que j'utilise toujours pour cela. Il le rend facile à mettre en œuvre des délais d'attente et est en mesure de traiter avec, inévitablement, la pendaison de processus.
(Cette solution a été testé avec Python 2.7.15)
Vous avez juste besoin de sys.la sortie standard stdout.flush() après chaque ligne de lecture/écriture: