Comment utiliser subprocess.Popen pour connecter plusieurs processus par des tuyaux?
Comment puis-je exécuter la commande shell suivante en utilisant le Python subprocess
module?
echo "input data" | awk -f script.awk | sort > outfile.txt
Les données d'entrée viendra à partir d'une chaîne, donc je n'ai pas réellement besoin echo
. Je suis arrivé jusqu'ici, est-ce que quelqu'un peut expliquer comment je l'ai fait passer à travers sort
trop?
p_awk = subprocess.Popen(["awk","-f","script.awk"],
stdin=subprocess.PIPE,
stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )
UPDATE: notez que même si la réponse acceptée ci-dessous ne répond pas à la question telle qu'elle est posée, je crois que S. Lott a raison et qu'il est préférable d'éviter d'avoir à résoudre le problème en premier lieu!
7 réponses
Vous seriez un peu plus heureux avec le suivant.
import subprocess
awk_sort = subprocess.Popen( "awk -f script.awk | sort > outfile.txt",
stdin=subprocess.PIPE, shell=True )
awk_sort.communicate( b"input data\n" )
déléguer une partie du travail au shell. Laissez connecter deux processus avec un pipeline.
vous seriez bien plus heureux de réécrire le script.il s'enfonce dans Python, éliminant awk et le pipeline.
Modifier. Certaines des raisons suggérant qu'awk n'aide pas.
[Il y a trop de raisons de répondre via commentaire.]
Awk ajoute une étape sans valeur significative. Il n'y a rien d'unique dans le traitement d'awk que Python ne gère pas.
la pipelinette de awk à sort, pour de grands ensembles de données, peut améliorer le temps de traitement écoulé. Pour de courtes séries de données, il n'a pas d'avantage significatif. Un rapide de mesure de
awk >file ; sort file
etawk | sort
révélera de simultanéité de l'aide. Avec le tri, il aide rarement parce que le tri n'est pas une filtrer.la simplicité du traitement" Python to sort "(au lieu de" Python to awk to sort") empêche le type exact de questions posées ici.
Python -- alors que wordier than awk -- est aussi explicite quand awk a certaines règles implicites qui sont opaques pour les débutants, et déroutantes pour les non-spécialistes.
Awk (comme le script shell lui-même) ajoute encore un autre langage de programmation. Si tout cela peut être fait dans un langage (Python), en éliminant le shell et la programmation awk élimine deux langages de programmation, permettant à quelqu'un de se concentrer sur les parties de la tâche qui produisent de la valeur.
conclusion: awk ne peut pas ajouter de valeur significative. Dans ce cas, awk est un coût net; il a ajouté suffisamment de complexité pour qu'il soit nécessaire de poser cette question. Supprimer awk sera un gain net.
barre latérale pourquoi construire un pipeline (a | b
) est donc dur.
Quand le shell est confronté à a | b
il doit faire la chose suivante.
fourche procédé enfant de la coquille d'origine. Cela deviendra éventuellement B.
construisez une pipe os. (pas un sous-processus Python.PIPE) but call
os.pipe()
qui renvoie deux nouveaux descripteurs de fichier connectés via common buffer. À ce stade, le processus a stdin, stdout, stderr de son parent, plus un fichier qui sera "stdout de A"" et "b stdin".fourche un enfant. L'enfant remplace son stdout par le nouveau stdout de A. Exec the
a
processus.l'Enfant B Ferme remplace sa ddin avec la nouvelle ddin du B. Exec the
b
processus.l'Enfant B attend a pour terminer.
le parent attend que b soit terminé.
je pense que le ci-dessus peut être utilisé récursivement pour frayer a | b | c
, mais vous devez implicitement mettre entre parenthèses les longs pipelines, les traitant comme s'ils étaient a | (b | c)
.
depuis Python a os.pipe()
,os.exec()
et os.fork()
, et vous pouvez remplacer sys.stdin
et sys.stdout
, il y a un moyen de faire ce qui précède en Python pur. En effet, vous pouvez être en mesure de travailler sur quelques raccourcis en utilisant os.pipe()
et subprocess.Popen
.
cependant, il est plus facile de déléguer cette opération au shell.
import subprocess
some_string = b'input_data'
sort_out = open('outfile.txt', 'wb', 0)
sort_in = subprocess.Popen('sort', stdin=subprocess.PIPE, stdout=sort_out).stdin
subprocess.Popen(['awk', '-f', 'script.awk'], stdout=sort_in,
stdin=subprocess.PIPE).communicate(some_string)
Pour émuler un oléoduc de shell:
from subprocess import check_call
check_call('echo "input data" | a | b > outfile.txt', shell=True)
sans invoquer le shell (voir 17.1.4.2. Remplacement du pipeline shell):
#!/usr/bin/env python
from subprocess import Popen, PIPE
a = Popen(["a"], stdin=PIPE, stdout=PIPE)
with a.stdin:
with a.stdout, open("outfile.txt", "wb") as outfile:
b = Popen(["b"], stdin=a.stdout, stdout=outfile)
a.stdin.write(b"input data")
statuses = [a.wait(), b.wait()] # both a.stdin/stdout are closed already
plumbum
fournit du sucre de syntaxe:
#!/usr/bin/env python
from plumbum.cmd import a, b # magic
(a << "input data" | b > "outfile.txt")()
analogique:
#!/bin/sh
echo "input data" | awk -f script.awk | sort > outfile.txt
est:
#!/usr/bin/env python
from plumbum.cmd import awk, sort
(awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()
http://www.python.org/doc/2.5.2/lib/node535.html couvrait assez bien cette question. Y a-t-il quelque chose que vous n'avez pas compris?
votre programme serait assez similaire, mais le second Popen
aurait stdout= à un fichier, vous n'avez pas besoin de la sortie de son .communicate()
.
inspiré de la réponse de @Cristian. J'ai rencontré le même problème, mais avec une commande Différente. Donc je mets mon exemple testé, qui je crois pourrait être utile:
grep_proc = subprocess.Popen(["grep", "rabbitmq"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
subprocess.Popen(["ps", "aux"], stdout=grep_proc.stdin)
out, err = grep_proc.communicate()
ceci est testé.
Ce qui a été fait
- déclaré paresseux
grep
exécution avec stdin de pipe. Cette commande sera exécutée aups
exécution de la commande quand le tuyau sera rempli avec le stdout deps
. - appelé la commande principale
ps
avec stdout dirigé vers le canal utilisé par legrep
la commande. - Grep communiqué pour sortir stdout du tuyau.
j'aime cette façon parce qu'il est naturel de tuyau de conception délicatement enveloppé avec subprocess
interfaces.
EDIT:pipes
est disponible sur Windows mais, surtout, ne semble pas réellement sur Windows. Voir les commentaires ci-dessous.
la bibliothèque standard de Python inclut maintenant pipes
module pour gérer cela:
https://docs.python.org/2/library/pipes.html, https://docs.python.org/3.4/library/pipes.html
Je ne sais pas combien de temps ce module a été autour, mais cette approche apparaît à être nettement plus simple que de coucher avec subprocess
.
les réponses précédentes ont omis un point important. Remplacement d'oléoduc de shell est fondamentalement correcte, comme le souligne geocar. C'est presque suffisant pour exécuter communicate
sur le dernier élément de la pipe.
le problème restant est de passer le données d'entrée au pipeline. Avec plusieurs sous-processus, un simple communicate(input_data)
sur le dernier élément ne fonctionne pas - il se bloque toujours. Vous devez créer un pipeline et d'un enfant manuellement comme ceci:
import os
import subprocess
input = """\
input data
more input
""" * 10
rd, wr = os.pipe()
if os.fork() != 0: # parent
os.close(wr)
else: # child
os.close(rd)
os.write(wr, input)
os.close(wr)
exit()
p_awk = subprocess.Popen(["awk", "{ print ; }"],
stdin=rd,
stdout=subprocess.PIPE)
p_sort = subprocess.Popen(["sort"],
stdin=p_awk.stdout,
stdout=subprocess.PIPE)
p_awk.stdout.close()
out, err = p_sort.communicate()
print (out.rstrip())
maintenant l'enfant fournit L'entrée par la pipe, et le parent appelle communicate (), ce qui fonctionne comme prévu. Avec cette approche, vous pouvez créer de longs pipelines arbitraires sans avoir recours à la "délégation d'une partie du travail au shell". Malheureusement, le documentation des sous-processus n'en parle pas.
il y a des moyens d'obtenir le même effet sans pipes:
from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)
maintenant utilisez stdin=tf
p_awk
. C'est un question de goût ce que vous préférez.
ce qui précède n'est toujours pas 100% équivalent à bash pipelines parce que la manipulation du signal est différente. Vous pouvez voir si vous ajoutez un autre tuyau élément qui tronque la sortie de sort
, e.g. head -n 10
. Avec le code ci-dessus, sort
affichera un message d'erreur" broken pipe " à stderr
. Vous ne verrez pas ce message lorsque vous exécutez le même pipeline dans le shell. (C'est la seule différence, le résultat stdout
est la même). Raison semble que python Popen
configure SIG_IGN
SIGPIPE
, alors que la coque laisse au SIG_DFL
et