Python - Comment passer une chaîne dans subprocess.Popen (en utilisant l'argument stdin)?

StackOverflow https://stackoverflow.com/questions/163542

  •  03-07-2019
  •  | 
  •  

Question

Si je fais ce qui suit:

import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0]

je reçois:

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 objet cStringIO.StringIO ne se rapproche pas suffisamment d'un fichier canard pour convenir à subprocess.Popen. Comment puis-je contourner ce problème?

Était-ce utile?

La solution

Popen.communicate () documentation:

  

Notez que si vous souhaitez envoyer des données à   processus stdin, vous devez   créer l'objet Popen avec   stdin = PIPE. De même, pour obtenir quelque chose   autre que Aucun dans le tuple de résultat,   vous devez donner stdout = PIPE et / ou   stderr = PIPE aussi.

     

Remplacement de os.popen *

    pipe = os.popen(cmd, 'w', bufsize)
    # ==>
    pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
  

Avertissement : utilisez communic () plutôt que   stdin.write (), stdout.read () ou   stderr.read () pour éviter les blocages dus   à l'un des autres tampons de tuyaux OS   remplir et bloquer l'enfant   processus.

Votre exemple pourrait donc être écrit comme suit:

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())
# -> four
# -> five
# ->

Dans la version actuelle de Python 3, vous pouvez utiliser le sous-processus .run , pour passer l’entrée sous forme de chaîne à une commande externe et obtenir son état de sortie et sa sortie sous forme de chaîne lors d’un appel:

#!/usr/bin/env python3
from subprocess import run, PIPE

p = run(['grep', 'f'], stdout=PIPE,
        input='one\ntwo\nthree\nfour\nfive\nsix\n', encoding='ascii')
print(p.returncode)
# -> 0
print(p.stdout)
# -> four
# -> five
# -> 

Autres conseils

J'ai compris cette solution de contournement:

>>> p = subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
>>> p.stdin.write(b'one\ntwo\nthree\nfour\nfive\nsix\n') #expects a bytes type object
>>> p.communicate()[0]
'four\nfive\n'
>>> p.stdin.close()

Y a-t-il un meilleur?

Je suis un peu surpris que personne n'ait suggéré de créer un canal, ce qui est à mon avis le moyen le plus simple de passer une chaîne à stdin d'un sous-processus:

read, write = os.pipe()
os.write(write, "stdin input here")
os.close(write)

subprocess.check_call(['your-command'], stdin=read)

Il existe une solution intéressante si vous utilisez Python version 3.4 ou supérieure. Utilisez l'argument input au lieu de l'argument stdin , qui accepte un argument d'octets:

output = subprocess.check_output(
    ["sed", "s/foo/bar/"],
    input=b"foo",
)

J'utilise python3 et j'ai découvert que vous deviez encoder votre chaîne avant de pouvoir la passer en stdin:

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
out, err = p.communicate(input='one\ntwo\nthree\nfour\nfive\nsix\n'.encode())
print(out)

"Apparemment, un objet cStringIO.StringIO n'est pas assez proche d'un fichier canard pour convenir à subprocess.Popen"

: -)

J'ai bien peur que non. Le canal est un concept de système d'exploitation de bas niveau, il nécessite donc absolument un objet fichier représenté par un descripteur de fichier au niveau du système d'exploitation. Votre solution de contournement est la bonne.

from subprocess import Popen, PIPE
from tempfile import SpooledTemporaryFile as tempfile
f = tempfile()
f.write('one\ntwo\nthree\nfour\nfive\nsix\n')
f.seek(0)
print Popen(['/bin/grep','f'],stdout=PIPE,stdin=f).stdout.read()
f.close()
"""
Ex: Dialog (2-way) with a Popen()
"""

p = subprocess.Popen('Your Command Here',
                 stdout=subprocess.PIPE,
                 stderr=subprocess.STDOUT,
                 stdin=PIPE,
                 shell=True,
                 bufsize=0)
p.stdin.write('START\n')
out = p.stdout.readline()
while out:
  line = out
  line = line.rstrip("\n")

  if "WHATEVER1" in line:
      pr = 1
      p.stdin.write('DO 1\n')
      out = p.stdout.readline()
      continue

  if "WHATEVER2" in line:
      pr = 2
      p.stdin.write('DO 2\n')
      out = p.stdout.readline()
      continue
"""
..........
"""

out = p.stdout.readline()

p.wait()

Notez que Popen.communicate (input = s) peut vous causer des problèmes si s est trop volumineux, car apparemment, le processus parent le mettra en mémoire tampon avant forger le sous-processus enfant, ce qui signifie qu'il a besoin de "deux fois plus". utilisé la mémoire à ce moment-là (au moins selon l'explication "sous le capot" et la documentation associée trouvées ici ). Dans mon cas particulier, s était un générateur qui a été complètement développé et écrit ensuite dans stdin , de sorte que le processus parent était énorme juste avant que l'enfant ne soit créé, et il ne restait plus de mémoire pour le bifurquer:

Fichier "/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py" ;, ligne 1130, dans _execute_child     self.pid = os.fork () OSError: [Errno 12] Impossible d'allouer de la mémoire

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
p.stdin.write('one\n')
time.sleep(0.5)
p.stdin.write('two\n')
time.sleep(0.5)
p.stdin.write('three\n')
time.sleep(0.5)
testresult = p.communicate()[0]
time.sleep(0.5)
print(testresult)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top