Je suis en essais sous-processus canalisations avec python. Je suis conscient que je peux faire ce que les programmes ci-dessous le faire en python directement, mais ce n'est pas le point. Je veux juste tester le pipeline donc je sais comment l'utiliser.
Mon système Linux Ubuntu 9.04 avec par défaut la version 2.6 de python.
J'ai commencé avec cette documentation exemple.
from subprocess import Popen, PIPE
p1 = Popen(["grep", "-v", "not"], stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
print output
Ce qui fonctionne, mais depuis p1
s' stdin
n'est pas redirigé, j'ai à taper des trucs dans le terminal pour nourrir la pipe. Quand je tape ^D
clôture stdin, je obtenir le résultat que je veux.
Cependant, je veux envoyer des données à la conduite à l'aide d'un python variable de chaîne. J'ai d'abord essayé d'écrire sur stdin:
p1 = Popen(["grep", "-v", "not"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
p1.stdin.write('test\n')
output = p2.communicate()[0] # blocks forever here
N'a pas fonctionné. J'ai essayé d'utiliser p2.stdout.read()
plutôt sur la dernière ligne, mais il bloque également. J'ai ajouté p1.stdin.flush()
et p1.stdin.close()
, mais il ne fonctionne pas non plus. J'ai Ensuite j'ai déménagé à communiquer:
p1 = Popen(["grep", "-v", "not"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
p1.communicate('test\n') # blocks forever here
output = p2.communicate()[0]
Donc c'est toujours pas ça.
J'ai remarqué que l'exécution d'un processus unique (comme p1
- dessus, en supprimant p2
) fonctionne parfaitement. Et en passant un descripteur de fichier pour p1
(stdin=open(...)
) fonctionne aussi. Le problème est donc de:
Est-il possible de transmettre des données à un pipeline de 2 ou plusieurs sous-processus en python, sans blocage? Pourquoi pas?
Je suis conscient que je pourrais exécuter un shell et exécuter le pipeline dans la coque, mais ce n'est pas ce que je veux.
Mise à JOUR 1: à la Suite d'Aaron Digulla l'astuce ci-dessous je vais maintenant essayer d'utiliser des threads pour le faire fonctionner.
Tout d'abord j'ai essayé de courir p1.communiquer sur un fil.
p1 = Popen(["grep", "-v", "not"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
t = threading.Thread(target=p1.communicate, args=('some data\n',))
t.start()
output = p2.communicate()[0] # blocks forever here
Ok, n'a pas fonctionné. Essayé d'autres combinaisons, comme la modifier afin de l' .write()
et également p2.read()
. Rien. Maintenant, nous allons essayer de l'approche inverse:
def get_output(subp):
output = subp.communicate()[0] # blocks on thread
print 'GOT:', output
p1 = Popen(["grep", "-v", "not"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
t = threading.Thread(target=get_output, args=(p2,))
t.start()
p1.communicate('data\n') # blocks here.
t.join()
le code se termine le blocage quelque part. Soit dans la donné naissance à fil, ou dans le thread principal, ou les deux. Si cela ne fonctionne pas. Si vous savez comment le faire fonctionner, il serait plus facile si vous pouvez fournir des code de travail. J'essaie ici.
Mise à JOUR 2
Paul Du Bois-réponse ci-dessous avec quelques informations, donc je n'ai plus de tests.
J'ai lu toute la subprocess.py
module et a obtenu de la façon dont il fonctionne. J'ai donc essayé d'appliquer exactement ce que fait le code.
Je suis sur linux, mais depuis que j'ai été le tester avec les fils, ma première démarche a été de reproduire l'exacte windows filetage code vu sur subprocess.py
s' communicate()
méthode, mais pour les deux processus au lieu d'un. Voici la liste complète de ce que j'ai essayé:
import os
from subprocess import Popen, PIPE
import threading
def get_output(fobj, buffer):
while True:
chunk = fobj.read() # BLOCKS HERE
if not chunk:
break
buffer.append(chunk)
p1 = Popen(["grep", "-v", "not"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
b = [] # create a buffer
t = threading.Thread(target=get_output, args=(p2.stdout, b))
t.start() # start reading thread
for x in xrange(100000):
p1.stdin.write('hello world\n') # write data
p1.stdin.flush()
p1.stdin.close() # close input...
t.join()
Bien. Il n'a pas de travail. Même après l' p1.stdin.close()
a été appelé, p2.stdout.read()
encore des blocs.
Ensuite, j'ai essayé la posix code sur subprocess.py
:
import os
from subprocess import Popen, PIPE
import select
p1 = Popen(["grep", "-v", "not"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["cut", "-c", "1-10"], stdin=p1.stdout, stdout=PIPE)
numwrites = 100000
to_read = [p2.stdout]
to_write = [p1.stdin]
b = [] # create buffer
while to_read or to_write:
read_now, write_now, xlist = select.select(to_read, to_write, [])
if read_now:
data = os.read(p2.stdout.fileno(), 1024)
if not data:
p2.stdout.close()
to_read = []
else:
b.append(data)
if write_now:
if numwrites > 0:
numwrites -= 1
p1.stdin.write('hello world!\n'); p1.stdin.flush()
else:
p1.stdin.close()
to_write = []
print b
Bloque aussi sur select.select()
. En diffusant print
s autour, j'ai trouvé ceci:
- La lecture est de travail. Code lit à de nombreuses reprises au cours de l'exécution.
- L'écriture est également à l'œuvre. Les données sont écrites à l'
p1.stdin
. - À la fin de l'
numwrites
,p1.stdin.close()
est appelé. - Lors de l'
select()
commence blocage, seulementto_read
a quelque chose,p2.stdout
.to_write
est déjà vide. -
os.read()
appel renvoie toujours à quelque chose, alorsp2.stdout.close()
n'est jamais appelée.
Conclusion à partir de deux tests: la Fermeture de la stdin
du premier processus sur le pipeline (grep
dans l'exemple) est de ne pas en faire de vidage de sa mise en mémoire tampon de sortie pour le prochain et de mourir.
Aucun moyen de le faire fonctionner?
PS: je ne veux pas utiliser un fichier temporaire, je l'ai déjà testé avec des fichiers et je sais que ça fonctionne. Et je ne veux pas utiliser windows.