Jak przekazać ciąg znaków do podprocesu.Popen (używając argumentu stdin)?

Jeśli wykonam następujące czynności:

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

Dostaję:

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'
Najwyraźniej cStringIO.Obiekt StringIO nie kwaknie wystarczająco blisko kaczki pliku, aby pasował do podprocesu.Popen. Jak mam to obejść?
Author: sshashank124, 2008-10-02

11 answers

Popen.communicate() dokumentacja:

Zwróć uwagę, że jeśli chcesz wysłać dane do Proces jest stdin, musisz Utwórz obiekt Popen z stdin=PIPE. Podobnie, aby uzyskać cokolwiek inne niż brak w wyniku krotki, musisz podać stdout=PIPE i / lub stderr = rura też.

Wymiana systemu operacyjnego.popen *

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

Warning Use communicate () zamiast stdin.write (), stdout.read () lub stderr.read (), aby uniknąć deadlocków z powodu do dowolnego innego systemu operacyjnego bufory rurowe napełnianie i blokowanie dziecka proces.

Więc twój przykład może być napisany w następujący sposób:

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

W Pythonie 3.5 + (3.6 + Dla encoding), możesz użyć subprocess.run, aby przekazać wejście jako łańcuch do zewnętrznego polecenia i uzyskać jego status wyjścia, a jego wyjście jako łańcuch z powrotem w jednym wywołaniu:

#!/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
# -> 
 348
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-08 06:45:10

Wymyśliłem to obejście:

>>> 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()
Czy jest lepszy?
 47
Author: Daryl Spitzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 00:31:40

Jestem trochę zaskoczony, że nikt nie zasugerował stworzenia rury, co jest moim zdaniem najprostszym sposobem na przekazanie łańcucha na stdin podprocesu:

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

subprocess.check_call(['your-command'], stdin=read)
 26
Author: Graham Christensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-02 16:34:03

Jest piękne rozwiązanie, jeśli używasz Pythona 3.4 lub lepszego. Użyj argumentu input zamiast argumentu stdin, który akceptuje argument bajtów:

output = subprocess.check_output(
    ["sed", "s/foo/bar/"],
    input=b"foo",
)
To działa na check_output oraz run, ale nie call lub check_call z jakiegoś powodu.
 23
Author: Flimm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-10 08:56:02

Używam python3 i dowiedziałem się, że musisz zakodować swój ciąg znaków, zanim będziesz mógł go przekazać na 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)
 13
Author: qed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-27 15:29:17

Najwyraźniej cStringIO.Obiekt StringIO nie kwaknie wystarczająco blisko kaczka plików do podprocesu.Popen

Obawiam się, że nie. Pipe jest koncepcją systemu operacyjnego niskiego poziomu, więc absolutnie wymaga obiektu file reprezentowanego przez deskryptor pliku na poziomie systemu operacyjnego. Twoje obejście jest właściwe.
 12
Author: Dan Lenski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-27 08:46:08
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()
 8
Author: Michael Waddell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 03:36:37

Uważaj, że Popen.communicate(input=s) może przysporzyć ci kłopotów, jeślisjest zbyt duży, ponieważ najwyraźniej proces rodzic buforuje go przed rozwidleniem podprocesu potomnego, co oznacza, że potrzebuje "dwa razy więcej" używanej pamięci w tym momencie (przynajmniej zgodnie z wyjaśnieniem "pod maską" i powiązaną dokumentacją znalezioną tutaj). W moim konkretnym przypadku, sbył generatorem, który najpierw został w pełni rozbudowany, a dopiero potem napisany dostdin, więc proces rodzica był ogromny tuż przed narodzeniem dziecka, a pamięć nie została, żeby ją rozwidlić:

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
Author: Lord Henry Wotton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:25
"""
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()
 5
Author: Lucien Hercaud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-08 16:15:27
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)
 2
Author: dusan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-27 14:01:45

W Pythonie 3.7 + zrób to:

my_data = "whatever you want\nshould match this f"
subprocess.run(["grep", "f"], text=True, input=my_data)

I prawdopodobnie będziesz chciał dodać capture_output=True, aby uzyskać wynik uruchomienia polecenia jako ciąg znaków.

W starszych wersjach Pythona zamień text=True na universal_newlines=True:

subprocess.run(["grep", "f"], universal_newlines=True, input=my_data)
 2
Author: Boris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-27 04:29:04