Python Popen, fermeture de flux et processus multiples
Question
J'ai des données que je voudrais gzip, uuencode puis imprimer en sortie standard. Ce que j'ai fondamentalement, c'est:
compressor = Popen("gzip", stdin = subprocess.PIPE, stdout = subprocess.PIPE)
encoder = Popen(["uuencode", "dummy"], stdin = compressor.stdout)
La façon dont je transmets des données au compresseur se fait par compress.stdin.write (matériel).
Ce que je dois vraiment faire est d’envoyer un EOF au compresseur, et je ne sais pas comment le faire.
À un moment donné, j'ai essayé compresser.stdin.close () mais cela ne fonctionne pas - cela fonctionne bien lorsque le compresseur écrit directement dans un fichier, mais dans le cas ci-dessus, le processus ne se termine pas et s'arrête on compressor.wait ().
Des suggestions? Dans ce cas, gzip est un exemple et je dois vraiment faire quelque chose avec la canalisation de la sortie d’un processus à un autre.
Remarque: les données que je dois compresser ne tiendront pas dans la mémoire. Par conséquent, la communication n'est pas vraiment une bonne option ici. Aussi, si je viens de courir
compressor.communicate("Testing")
après les 2 lignes ci-dessus, il se bloque toujours avec l'erreur
File "/usr/lib/python2.4/subprocess.py", line 1041, in communicate rlist, wlist, xlist = select.select(read_set, write_set, [])
La solution
Je suppose que le problème vient de l'ordre dans lequel vous ouvrez les tuyaux. UUEncode est amusant, c’est qu’il gémira quand vous le lancerez s’il n’ya pas de pipe entrante dans le bon sens (essayez de lancer le vêtement par soi-même dans un appel Popen pour voir l’explosion avec juste PIPE comme stdin et stdout)
Essayez ceci:
encoder = Popen(["uuencode", "dummy"], stdin=PIPE, stdout=PIPE)
compressor = Popen("gzip", stdin=PIPE, stdout=encoder.stdin)
compressor.communicate("UUencode me please")
encoded_text = encoder.communicate()[0]
print encoded_text
begin 644 dummy
F'XL(`%]^L$D``PL-3<U+SD])5<A-52C(24TL3@4`;2O+"!(`````
`
end
Vous avez raison, btw ... il n’ya aucun moyen d’envoyer un fichier EOF générique dans un canal. Après tout, chaque programme définit vraiment son propre fichier EOF. Pour ce faire, fermez le tuyau, comme vous tentiez de le faire.
EDIT: Je devrais être plus clair à propos de uuencode. En tant que programme shell, son comportement par défaut est d’attendre une entrée de la console. Si vous le lancez sans "live" tuyau entrant, il bloquera l’attente pour l’entrée de la console. En ouvrant le codeur en second lieu, avant que vous ayez envoyé du matériel dans le tuyau du compresseur, le codeur bloquait en attendant que vous commenciez à taper. Jerub avait raison en ce qu'il bloquait quelque chose.
Autres conseils
Ce n’est pas le genre de chose que vous devriez faire directement en python, il ya des excentricités concernant le fonctionnement des choses qui en font une bien meilleure idée de le faire avec un shell. Si vous ne pouvez utiliser que subprocess.Popen ("foo | bar", shell = True), tant mieux.
Ce qui pourrait arriver, c’est que gzip n’a pas encore été en mesure de générer toutes ses entrées et que le processus ne se terminera pas tant que ses écritures stdout ne seront pas terminées.
Vous pouvez voir quel appel système un processus bloque si vous utilisez strace. Utilisez ps auxwf
pour découvrir quel processus correspond au processus gzip, puis utilisez strace -p $ pidnum
pour voir quel appel système il est exécuté. Notez que stdin est FD 0 et stdout est FD 1, vous le verrez probablement en lecture ou en écriture sur ces descripteurs de fichier.
si vous souhaitez simplement compresser et n'avez pas besoin des wrappers, envisagez d'utiliser le module zlib
import zlib
compressed = zlib.compress("text")
pourquoi les suggestions shell = True et Unix ne fonctionnent-elles pas?
from subprocess import *
pipes = Popen("gzip | uuencode dummy", stdin=PIPE, stdout=PIPE, shell=True)
for i in range(1, 100):
pipes.stdin.write("some data")
pipes.stdin.close()
print pipes.stdout.read()
semble fonctionner