Pour lancer des programmes à partir de mes scripts Python, j'utilise la méthode suivante:
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
Ainsi, lorsque je lance un processus comme Process.execute("mvn clean install")
, mon programme attend que le processus soit terminé et ce n'est qu'alors que j'obtiens la sortie complète de mon programme. C'est ennuyeux si j'exécute un processus qui prend du temps pour se terminer.
Puis-je laisser mon programme écrire la sortie de processus ligne par ligne, en interrogeant la sortie de processus avant qu'elle ne se termine en boucle ou quelque chose?
** [EDIT] Désolé, je n'ai pas bien cherché avant de poster cette question. L'enfilage est en fait la clé. Trouvé un exemple ici qui montre comment le faire: ** Sous-processus Python.Ouvrir à partir d'un thread
la source
Réponses:
Vous pouvez utiliser iter pour traiter les lignes dès que les sorties de commande: eux
lines = iter(fd.readline, "")
. Voici un exemple complet montrant un cas d'utilisation typique (merci à @jfs pour son aide):la source
for line in popen.stdout: print(line.decode(), end='')
. Pour prendre en charge Python 2 et 3, utilisez littéral octets:b''
sinon,lines_iterator
ne se termine jamais sur Python 3.bufsize=1
(cela peut améliorer les performances sur Python 2), fermerpopen.stdout
explicitement le canal (sans attendre que le garbage collection s'en occupe), et augmentersubprocess.CalledProcessError
(commecheck_call()
,check_output()
faire). L'print
instruction est différente sur Python 2 et 3: vous pouvez utiliser le hack de softspaceprint line,
(note: virgule) pour éviter de doubler toutes les nouvelles lignes comme le fait votre code et de passeruniversal_newlines=True
sur Python 3, pour obtenir du texte au lieu d'octets - réponse liée .execute(["python", "-u", "child_thread.py"])
. Plus d'infos: stackoverflow.com/questions/14258500/…Ok, j'ai réussi à le résoudre sans threads (toute suggestion pourquoi l'utilisation de threads serait mieux appréciée) en utilisant un extrait de cette question Interception de la sortie standard d'un sous-processus pendant son exécution
la source
print line,
poursys.stdout.write(nextline); sys.stdout.flush()
. Sinon, il s'imprimerait toutes les deux lignes. Là encore, cela utilise l'interface Notebook d'IPython, alors peut-être que quelque chose d'autre se passait - indépendamment, appelant explicitementflush()
works.stderr=subprocess.STDOUT
àstderr=subprocess.PIPE
puis appelle àprocess.stderr.readline()
partir de la boucle, je semble aller à l'encontre du blocage même qui est averti dans la documentation dusubprocess
module.stdout=subprocess.PIPE,stderr=subprocess.STDOUT
c'est capturer stderr, et je crois (mais je n'ai pas testé) qu'il capture également stdin.Pour imprimer la sortie ligne du sous-processus ligne par ligne dès que son tampon stdout est vidé en Python 3:
Remarque: vous n'avez pas besoin
p.poll()
- la boucle se termine lorsque eof est atteint. Et vous n'avez pas besoiniter(p.stdout.readline, '')
- le bogue de lecture anticipée est corrigé dans Python 3.Voir aussi, Python: lire l'entrée de streaming depuis subprocess.communicate () .
la source
sys.stdout.flush()
dans le parent - stdout est mis en mémoire tampon s'il n'est pas redirigé vers un fichier / pipe et donc l'impressionline
vide automatiquement le tampon. Vous n'avez pas besoinsys.stdout.flush()
de l'enfant aussi - passez-u
plutôt l'option de ligne de commande.>
puis exécutezpython -u your-script.py > some-file
. Remarque:-u
option que j'ai mentionnée ci-dessus (pas besoin d'utilisersys.stdout.flush()
).p.wait()
- il est appelé à la sortie duwith
bloc. Utilisezp.returncode
.Il existe en fait un moyen très simple de le faire lorsque vous souhaitez simplement imprimer la sortie:
Ici, nous pointons simplement le sous-processus vers notre propre sortie standard et utilisons l'api de réussite ou d'exception existante.
la source
shell=True
@tokland
essayé votre code et corrigé pour 3.4 et windows dir.cmd est une simple commande dir, enregistrée en tant que fichier cmd
la source
iter()
etend='\r\n'
sont inutiles. Python utilise le mode de saut de ligne universel par défaut, c'est-à-dire que tout'\n'
est traduit'\r\n'
pendant l'impression.'latin'
est probablement un mauvais encodage, vous pouvez utiliseruniversal_newlines=True
pour obtenir la sortie de texte en Python 3 (décodé en utilisant l'encodage préféré des paramètres régionaux). Ne vous arrêtez pas.poll()
, il pourrait y avoir des données non lues en mémoire tampon. Si le script Python s'exécute dans une console, sa sortie est mise en mémoire tampon de ligne; vous pouvez forcer la mise en mémoire tampon de ligne en utilisant l'-u
option - vous n'avez pas besoinflush=True
ici.Dans le cas où quelqu'un veut lire les deux
stdout
etstderr
en même temps en utilisant des threads, voici ce que j'ai trouvé:Je voulais juste partager cela, car je me suis retrouvé sur cette question en essayant de faire quelque chose de similaire, mais aucune des réponses n'a résolu mon problème. Espérons que cela aide quelqu'un!
Notez que dans mon cas d'utilisation, un processus externe tue le processus que nous
Popen()
.la source
Pour quiconque essaie de répondre à cette question pour obtenir la sortie standard à partir d'un script Python, notez que Python met en mémoire tampon sa sortie standard, et donc cela peut prendre un certain temps pour voir la sortie standard.
Cela peut être corrigé en ajoutant les éléments suivants après chaque écriture stdout dans le script cible:
la source
import
l'autre script; regardezmultiprocessing
outhreading
si vous avez besoin d'une exécution parallélisée.subprocess.run("/path/to/python/executable", "pythonProgramToRun.py")
En Python> = 3.5 en utilisant des
subprocess.run
œuvres pour moi:(obtenir la sortie pendant l'exécution fonctionne également sans
shell=True
) https://docs.python.org/3/library/subprocess.html#subprocess.runla source
subprocess.run()
appel ne revient que lorsque le sous-processus a fini de s'exécuter.>>> import subprocess; subprocess.run('top')
semble également imprimer "pendant l'exécution" (et le dessus ne se termine jamais). Peut-être que je ne saisis pas une subtile différence?stdout=subprocess.PIPE
vous ne pouvez la lire qu'après avoirtop
terminé. Votre programme Python est bloqué lors de l'exécution du sous-processus.run
méthode fonctionne toujours si vous souhaitez uniquement voir la sortie telle qu'elle est générée. Si vous voulez faire quelque chose avec la sortie en python de manière asynchrone, vous avez raison, cela ne fonctionne pas.Pour répondre à la question d'origine, la meilleure façon pour IMO est simplement de rediriger le sous-processus
stdout
directement vers votre programmestdout
(en option, la même chose peut être faite pourstderr
, comme dans l'exemple ci-dessous)la source
stdout
etstderr
fait la même chose avec moins de code. Bien que je suppose qu'explicite vaut mieux qu'implicite.Ce PoC lit en permanence la sortie d'un processus et est accessible en cas de besoin. Seul le dernier résultat est conservé, toutes les autres sorties sont ignorées, ce qui empêche le PIPE de se développer hors de la mémoire:
print_date.py
sortie: Vous pouvez clairement voir qu'il n'y a qu'une sortie de ~ 2,5 s d'intervalle rien entre les deux.
la source
Cela fonctionne au moins en Python3.4
la source
Aucune des réponses ici ne répond à tous mes besoins.
Un peu de contexte: j'utilise un ThreadPoolExecutor pour gérer un pool de threads, chacun lançant un sous-processus et les exécutant simultanément. (En Python2.7, mais cela devrait également fonctionner dans la version 3.x plus récente). Je ne veux pas utiliser de threads uniquement pour la collecte de sortie car je veux autant de disponibles que possible pour d'autres choses (un pool de 20 processus utiliserait 40 threads juste pour s'exécuter; 1 pour le thread de processus et 1 pour stdout ... et plus si vous voulez stderr je suppose)
Je retire beaucoup d'exceptions et telles ici, donc cela est basé sur du code qui fonctionne en production. J'espère que je ne l'ai pas gâché dans le copier-coller. De plus, les commentaires sont les bienvenus!
Je suis sûr que des frais généraux sont ajoutés ici, mais ce n'est pas un problème dans mon cas. Fonctionnellement, il fait ce dont j'ai besoin. La seule chose que je n'ai pas résolue est la raison pour laquelle cela fonctionne parfaitement pour les messages de journal, mais je vois certains
print
messages apparaître plus tard et en même temps.la source
En Python 3.6, j'ai utilisé ceci:
la source
subprocess.call()
a quelques verrues qui sont corrigées par de nouvelles fonctions; en Python 3.6, vous utiliseriez généralementsubprocess.run()
cela; pour plus de commodité, l'ancienne fonction wrappersubprocess.check_output()
est également toujours disponible - elle renvoie la sortie réelle du processus (ce code retournerait uniquement le code de sortie, mais même à la place, imprime quelque chose d'indéfini).