Python - Comment puis-je passer une chaîne en sous-processus.Popen (à l'aide de la stdin argument)?
Si je ne les suivants:
import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0]
J'obtiens:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 533, in __init__
(p2cread, p2cwrite,
File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 830, in _get_handles
p2cread = stdin.fileno()
AttributeError: 'cStringIO.StringI' object has no attribute 'fileno'
Apparemment un cStringIO.StringIO objet n'a pas de couac assez près pour un fichier de canard à la fonction de sous-processus.Popen. Comment puis-je contourner cela?
- Au lieu de la contestation de ma réponse avec cet être supprimé, je vais l'ajouter comme commentaire... lecture Recommandée: Doug Hellmann's Module Python de la Semaine post de blog sur le sous-processus.
- le blog contient plusieurs erreurs par exemple, le premier exemple de code:
call(['ls', '-1'], shell=True)
est incorrect. Je vous recommande de lire questions fréquentes de sous-processus' tag description> à la place. En particulier, Pourquoi les sous-processus.Popen ne fonctionne pas lorsque args est la séquence? explique pourquoicall(['ls', '-1'], shell=True)
est faux. Je me souviens de laisser des commentaires sous l'article du blog, mais je ne les vois pas maintenant pour une raison quelconque.
Vous devez vous connecter pour publier un commentaire.
Popen.communiquer()
documentation:De sorte que votre exemple peut être écrite comme suit:
Sur l'actuel Python version 3, vous pouvez utiliser
sous-processus.run
, pour passer d'entrée comme une chaîne de caractères à une commande extérieure et d'obtenir son statut de sortie, et sa sortie comme une chaîne de caractères en arrière dans un appel:p.communicate(input=b'one\n')
. Je sais que nous sommes d'écriture à processus enfantstdin
. Mais, Est-parent processus d'écriture thru c'eststdout
de processus enfantstdin
? Pouvez-vous nous expliquer comme ceci, dans votre réponse?b'something'
) à grep de stdin?p.stdin
avec toutes les méthodes habituelles: .write(), .flush(), .fileno(), .close().input
arg avecsubprocess.run()
. Les anciennes versions de python3 travail si vous faites cela:p = run(['grep', 'f'], stdout=PIPE, input=some_string.encode('ascii'))
.encode()
-- le code à utiliserencoding
paramètre 3- "en cours de Python 3 version" appelé Python 3.6. Il est Python 3.7 maintenant.encoding
, mon exemple, travaille sur le langage Python 3.5. Vous avez besoin de python 3.6 utilisation de laencoding
arg avecsubprocess.run()
. Ce qui est pratique puisque tout le monde est en cours d'exécution avec Python 3.6 encore, par exemple Debian Stable est sur Python 3.5.J'ai pensé à cette solution de contournement:
Est-il une meilleure?
p.communicate()
doit être utilisé. Voir ma réponse.communicate
va fermer la conduite et laisser le processus de fin gracieusement.Je suis un peu surpris de voir que personne ne propose la création d'un tuyau, qui est à mon avis beaucoup plus simple pour passer d'une chaîne à l'entrée standard stdin d'un sous-processus:
os
et lasubprocess
de la documentation à la fois d'accord que vous devriez préférer ce dernier par-dessus l'ancienne. C'est une solution qui a un (un peu moins concis) standard de remplacement; la accepté de répondre à des citations de la documentation pertinente.Il y a une belle solution si vous êtes à l'aide de Python 3.4 ou mieux. Utiliser le
input
argument au lieu de lastdin
argument, qui accepte octets argument:check_output
devrait avoir uninput
argument, mais pascall
.check_call
mais il travaille pour lerun
. Il travaille également avec l'entrée=chaîne, tant que vous passez un encodage argument trop conformément à la documentation.J'utilise python3 et a découvert que vous devez encoder votre chaîne avant de la passer dans stdin:
b'something'
). Il sera de retour err et comme octets également. Si vous voulez éviter cela, vous pouvez passeruniversal_newlines=True
àPopen
. Puis elle accepte en entrée comme les str et sera de retour err/comme str également.universal_newlines=True
sera également convertir vos retours à la ligne pour correspondre à votre système"Apparemment un cStringIO.StringIO objet n'a pas de couac assez près pour un fichier de canard à la fonction de sous-processus.Popen"
🙂
Je crains que non. Le tuyau est de faible niveau de l'OS concept, de sorte qu'il a absolument besoin d'un fichier objet qui est représenté par un niveau de l'OS du descripteur de fichier. Votre solution est la bonne.
shell=True
est donc couramment utilisé pour aucune bonne raison, et c'est une question populaire, permettez-moi de souligner qu'il y a beaucoup de situations oùPopen(['cmd', 'with', 'args'])
est décidément mieux quePopen('cmd with args', shell=True)
et avoir le shell briser la commande et les arguments en jetons, mais pas de fournir quelque chose d'utile, alors que l'ajout d'une quantité importante de complexité et donc de la surface d'attaque.Attention,
Popen.communicate(input=s)
peut vous causer des ennuis sis
est trop grand, apparemment parce que le parent processus de tampon, il avant un fork de l'enfant sous-processus, le sens qu'il a besoin de "deux fois plus" de mémoire utilisée à ce point (au moins selon le "sous le capot" de l'explication et de la documentation liée trouvé ici). Dans mon cas particulier,s
est un générateur qui a d'abord été complètement développé, et alors seulement, écrit àstdin
de sorte que le processus parent était énorme, juste avant que l'enfant a été engendré,et aucune mémoire n'a été laissé à la fourchette c':
File "/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py", line 1130, in _execute_child
self.pid = os.fork()
OSError: [Errno 12] Cannot allocate memory