2009-09-11 6 views
12

J'ai une question générale sur popen (et toutes les fonctions connexes), applicable à tous les systèmes d'exploitation, lorsque j'écris un script python ou un code c et exécute l'exécutable résultant la console (win ou linux), je peux voir immédiatement la sortie du processus. Toutefois, si j'exécute le même exécutable en tant que processus bifurqué avec sa sortie stdout redirigée dans un canal, la sortie est mise en mémoire tampon, généralement jusqu'à 4 096 octets avant d'être écrite sur le canal où le processus parent peut la lire.Contournement de la sortie de sous-processus avec popen en C ou Python

Le script python suivant générer une sortie en morceaux de 1 024 octets

import os, sys, time 

if __name__ == "__main__": 
    dye = '@'*1024 
    for i in range (0,8): 
     print dye 
     time.sleep(1) 

Le script python suivant exécutera le script précédent et de lire la sortie dès qu'il vient à la conduite, octet par octet

import os, sys, subprocess, time, thread 

if __name__ == "__main__": 
    execArgs = ["c:\\python25\\python.exe", "C:\\Scripts\\PythonScratch\\byte_stream.py"] 

    p = subprocess.Popen(execArgs, bufsize=0, stdout=subprocess.PIPE) 
    while p.returncode == None: 
     data = p.stdout.read(1) 
     sys.stdout.write(data) 
     p.poll() 

Ajustez le chemin d'accès à votre système d'exploitation. Lors de l'exécution dans cette configuration, la sortie n'apparaîtra pas en blocs de 1024 mais en blocs de 4096, malgré la taille de la mémoire tampon de la commande popen définie sur 0 (qui est par défaut). Quelqu'un peut-il me dire comment changer ce comportement ?, est-il possible de forcer le système d'exploitation à traiter la sortie du processus forké de la même manière que lorsqu'il est exécuté à partir de la console? sans tampon?

Répondre

14

En général, la bibliothèque d'exécution C standard (qui s'exécute pour presque tous les programmes sur chaque système, plus ou moins ;-) détecte si stdout est un terminal ou non; sinon, il tamponne la sortie (ce qui peut être un énorme gain d'efficacité, comparé à la sortie sans tampon). Si vous contrôlez le programme en cours d'écriture, vous pouvez (comme une autre réponse suggérée) vider stdout de façon continue, ou (plus élégamment si possible) essayer de forcer stdout à ne pas l'endommager, par ex. en exécutant Python avec le drapeau -u commandline:

-u  : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x) 
     see man page for details on internal buffering relating to '-u' 

(ce que la page de manuel ajoute une mention est de stdin et des problèmes avec le mode binaire [s]). Si vous ne pouvez pas ou ne voulez pas toucher le programme en cours d'écriture, -u ou similaire sur le programme en cours de lecture est peu susceptible d'aider (la mise en mémoire tampon la plus importante est celle qui se passe sur la sortie stdout de l'auteur, pas celui sur le stdin du lecteur). L'alternative est de tromper l'auteur en lui faisant croire qu'il écrit sur un terminal (même s'il écrit en fait dans un autre programme!), Via le module de bibliothèque standard pty ou le module tiers de plus haut niveau pexpect (ou, pour Windows, son port wexpect).

+0

j'ai essayé de jouer avec -u, pas de joie mais le projet semble prometteur, merci! –

+0

juste pour suivre, pexpect fonctionne comme un charme, wexpect est un peu buggy (et difficile à trouver), mais fait le travail. C'est là que j'ai trouvé la version la plus récente de wexpect: http://sage.math.washington.edu/home/goreckc/sage/wexpect/ –

+0

Merci Gearoid, en effet curieux que wexpect n'a pas été mis à jour à la dernière version sur son code.google.com home, je me demande pourquoi! –

1

C'est correct, et s'applique à la fois à Windows et Linux (et éventuellement d'autres systèmes), avec popen() et fopen(). Si vous souhaitez que le tampon de sortie soit distribué avant 4096 octets, utilisez fflush() (sur C) ou sys.stdout.flush() (Python).

+0

oui, c'est ce que je fais en ce moment, mais ma situation de travail signifie que le processus générant la sortie est défini par l'utilisateur, j'aurais dû mentionner que dans la question initiale –

Questions connexes