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. Как мне обойти это?

875   10  

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

    Ничего не найдено.