Python-как передать строку в подпроцесс.К popen (с использованием стандартного ввода аргумента)?
Если я сделаю следующее:
import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('onentwonthreenfournfivensixn')).communicate()[0]
Я:
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'
по-видимому, cStringIO.Объект StringIO не крякает достаточно близко к утке файла, чтобы удовлетворить подпроцесс.К popen. Как мне обойти это?
10 ответов:
Popen.communicate()документы:обратите внимание, что если вы хотите отправить данные процесс stdin, вам нужно создайте объект Popen с помощью stdin=труба. Точно так же, чтобы получить что-нибудь кроме None в результирующем кортеже, вам нужно дать stdout=PIPE и / или stderr=труба тоже.
замена ОС.попен*
pipe = os.popen(cmd, 'w', bufsize) # ==> pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdinпредупреждение используйте communicate (), а не стандартный ввод.писать(), стандартный вывод.читать или поток stderr.читать (), чтобы избежать взаимоблокировок из-за к любому из других буферов каналов ОС заполнение и блокирование ребенка процесс.
так что ваш пример может быть записан следующим образом:
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 # ->
в текущей версии Python 3 Вы можете использовать
subprocess.run, чтобы передать ввод в виде строки во внешнюю команду и получить ее статус выхода, а ее вывод в виде строки обратно в один вызов:#!/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 # ->
Я понял этот метод:
>>> 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()есть ли лучший?
Я немного удивлен, что никто не предложил создать канал, который, на мой взгляд, является самым простым способом передать строку в stdin подпроцесса:
read, write = os.pipe() os.write(write, "stdin input here") os.close(write) subprocess.check_call(['your-command'], stdin=read)
Я использую python3 и выяснил, что вам нужно кодировать свою строку, прежде чем вы сможете передать ее в 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)
по-видимому, cStringIO.Объект StringIO не крякает достаточно близко к утке файла, чтобы удовлетворить подпроцесс.К popen"
: -)
боюсь, что нет. Канал является низкоуровневой концепцией ОС, поэтому для него абсолютно необходим файловый объект, представленный файловым дескриптором уровня ОС. Ваше решение является правильным.
есть красивое решение, если вы используете Python 3.4 или лучше. Используйте вместо
stdinаргумент, который принимает аргумент байт:output = subprocess.check_output( ["sed", "s/foo/bar/"], input=b"foo", )
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()
помните, что
Popen.communicate(input=s)может дать вам неприятности, еслиsслишком большой, потому что, по-видимому, родительский процесс будет буферизировать его до разветвление дочернего подпроцесса, что означает, что ему нужно "в два раза больше" используемой памяти в этот момент (по крайней мере, согласно объяснению "под капотом" и связанной документации, найденной здесь). В моем конкретном случае,sбыл генератор, который был сначала полностью расширен и только потом записан вstdinтаким образом, родительский процесс был огромным правом прежде чем ребенок был порожден, и не осталось памяти, чтобы раскошелиться:
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)
Comments