it-swarm.com.de

Python - Wie übergebe ich einen String an subprocess.Popen (mit dem Argument stdin)?

Wenn ich folgendes mache:

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

Ich bekomme:

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'

Anscheinend quakt ein cStringIO.StringIO-Objekt nicht nah genug an einer Datei, um dem Subprozess zu entsprechen. Wie kann ich das umgehen?

258
Daryl Spitzer

Popen.communicate() Dokumentation:

Beachten Sie, dass Sie, wenn Sie Daten an die stdin des Prozesses senden möchten, das Popen-Objekt mit stdin = PIPE erstellen müssen. In ähnlicher Weise müssen Sie auch stdout = PIPE und/oder stderr = PIPE angeben, um etwas anderes als None im Ergebnis-Tupel zu erhalten.

os.popen ersetzen *

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

Warnung Verwenden Sie communic () anstelle von stdin.write (), stdout.read () oder stderr.read (), um Deadlocks aufgrund eines der folgenden Probleme zu vermeiden andere OS-Pipe-Puffer füllen und blockieren den untergeordneten Prozess.

So könnte Ihr Beispiel wie folgt geschrieben werden:

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
# ->

In der aktuellen Python 3-Version könnten Sie subprocess.run , um die Eingabe als Zeichenfolge an einen externen Befehl zu übergeben und den Exit-Status sowie die Ausgabe als Zeichenfolge in einem einzigen Aufruf abzurufen:

#!/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
# -> 
306
jfs

Ich habe diese Problemumgehung herausgefunden:

>>> 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()

Gibt es eine bessere?

43
Daryl Spitzer

Ich bin ein bisschen überrascht, dass niemand vorgeschlagen hat, eine Pipe zu erstellen, was meiner Meinung nach der einfachste Weg ist, eine Zeichenfolge an stdin eines Unterprozesses zu übergeben:

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

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

Es gibt eine schöne Lösung, wenn Sie Python 3.4 oder besser verwenden. Verwenden Sie das Argument input anstelle des Arguments stdin, das ein bytes-Argument akzeptiert:

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

Ich benutze Python3 und habe herausgefunden, dass Sie Ihren String verschlüsseln müssen, bevor Sie ihn an stdin übergeben können:

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)
14
qed

"Anscheinend quakt ein cStringIO.StringIO-Objekt nicht nah genug an einer Datei, um dem Subprozess zu entsprechen. Popen"

:-)

Ich fürchte nein. Die Pipe ist ein Betriebssystemkonzept auf niedriger Ebene, daher ist unbedingt ein Dateiobjekt erforderlich, das durch einen Dateideskriptor auf Betriebssystemebene dargestellt wird. Ihre Problemumgehung ist die richtige.

13
Dan Lenski
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()
7
Michael Waddell
"""
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()
6
Lucien Hercaud

Beachten Sie, dass Popen.communicate(input=s) Ihnen Probleme bereiten kann, wenn s zu groß ist, da der übergeordnete Prozess ihn anscheinend puffern wird , bevor die untergeordneter Unterprozess, was bedeutet, dass an diesem Punkt "doppelt so viel" belegter Speicher benötigt wird (zumindest gemäß der Erklärung "unter der Haube" und der verknüpften Dokumentation hier ). In meinem speziellen Fall war s ein Generator, der zuerst vollständig erweitert und dann erst in stdin geschrieben wurde, sodass der übergeordnete Prozess sehr umfangreich war, bevor das untergeordnete Element erzeugt wurde, und kein Speicher zum Verzweigen übrig blieb:

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

5
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)
3
gedwarp