Python - Wie gebe ich einen String in subprocess.Popen (das stdin Argument)?
-
03-07-2019 - |
Frage
Wenn ich Folgendes tun:
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:
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'
Offenbar ein cStringIO.StringIO Objekt nahe genug nicht in eine Datei Ente quakt subprocess.Popen zu entsprechen. Wie kann ich dieses Problem umgehen?
Lösung
Popen.communicate()
Dokumentation:
Beachten Sie, dass, wenn Sie Daten senden möchten der Prozess des stdin, müssen Sie Erstellen Sie die Popen Objekt mit stdin = PIPE. In ähnlicher Weise zu bekommen alles anders als Keiner im Ergebnis Tupel, Sie brauchen stdout = PIPE und / oder geben stderr = PIPE zu.
Ersetzen os.popen *
pipe = os.popen(cmd, 'w', bufsize)
# ==>
pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
Warnung Verwenden Sie kommunizieren () statt stdin.write (), stdout.read () oder stderr.read () Deadlocks zu vermeiden, aufgrund zu einem der anderen OS Rohrpuffer Auffüllen und das Kind blockiert 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
# ->
Auf der aktuellen Python 3-Version, können Sie subprocess.run
, Eingabe als String an einen externen Befehl übergeben und ihren Exit-Status erhalten, und seine Ausgabe als String in einem Aufruf zurück:
#!/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
# ->
Andere Tipps
ich dachte, diese Abhilfe aus:
>>> 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?
Ich bin ein wenig überrascht, niemand vorgeschlagen, ein Rohr zu schaffen, die meiner Meinung nach ist der weit einfachste Weg, um eine Zeichenfolge zu stdin eines subprocess weitergeben müssen:
read, write = os.pipe()
os.write(write, "stdin input here")
os.close(write)
subprocess.check_call(['your-command'], stdin=read)
Es gibt eine schöne Lösung, wenn Sie verwenden Python 3.4 oder besser. Verwenden Sie das input
Argument anstelle des stdin
Argument, das ein Byte Argument akzeptiert:
output = subprocess.check_output(
["sed", "s/foo/bar/"],
input=b"foo",
)
Ich bin mit python3 und fand heraus, dass Sie Ihre Zeichenfolge kodieren, bevor Sie es in stdin passieren kann:
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)
„Anscheinend ein cStringIO.StringIO Objekt schließen sich nicht auf eine Datei Ente quaken genug subprocess.Popen passen“
: -)
Ich fürchte nicht. Das Rohr ist ein Low-Level-OS Konzept, so dass es erfordert unbedingt ein Datei-Objekt, das von einer OS-Ebene Dateideskriptors dargestellt wird. Ihre Abhilfe ist, die richtige ist.
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()
Beachten Sie, dass Popen.communicate(input=s)
may Sie Probleme geben ifs
is zu groß, denn anscheinend wird der übergeordnete Prozess es Puffer vor das Kind subprocess gabeln, was bedeutet, es an diesem Punkt „doppelt so viel“ gebrauchte Speicher benötigt (mindestens nach der „unter der Haube“ Erklärung und verknüpfte Dokumentation ). In meinem speziellen Fall s
was einen Generator, die ersten voll ausgebaut wurde und erst dann geschrieben tostdin
so der übergeordnete Prozess riesig richtig war, bevor das Kind hervorgebracht wurde,
und kein Speicher übrig war, um es zu Gabel:
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
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)