2013-11-09 2 views
12

У меня есть программа, которая взаимодействует с пользователем (действует как оболочка), и я хочу запустить ее с помощью модуля подпроцесса python в интерактивном режиме. Это означает, что я хочу, чтобы писать на stdin и сразу получить вывод из stdout. Я попробовал множество решений, предлагаемых здесь, но никто из них, похоже, не работает для моих нужд.Интерактивный ввод/вывод с использованием python

Кода я написал на основе Running an interactive command from within python

import Queue 
import threading 
import subprocess 
def enqueue_output(out, queue): 
    for line in iter(out.readline, b''): 
     queue.put(line) 
    out.close() 

def getOutput(outQueue): 
    outStr = '' 
    try: 
     while True: #Adds output from the Queue until it is empty 
      outStr+=outQueue.get_nowait() 

    except Queue.Empty: 
     return outStr 

p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize = 1) 
#p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True) 

outQueue = Queue() 
errQueue = Queue() 

outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue)) 
errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue)) 

outThread.daemon = True 
errThread.daemon = True 

outThread.start() 
errThread.start() 

p.stdin.write("1\n") 
p.stdin.flush() 
errors = getOutput(errQueue) 
output = getOutput(outQueue) 

p.stdin.write("5\n") 
p.stdin.flush() 
erros = getOutput(errQueue) 
output = getOutput(outQueue) 

Проблема заключается в том, что очередь остается пустой, как будто нет выхода. Только если я пишу, чтобы написать все входные данные, которые программа должна выполнить и завершить, тогда я получаю вывод (что не то, что я хочу). Например, если я что-то типа:

p.stdin.write("1\n5\n") 
errors = getOutput(errQueue) 
output = getOutput(outQueue) 

Есть ли способ сделать то, что я хочу?

EDIT: Сценарий запускается на машине Linux. Я сменил свой сценарий и удалил universal_newlines = True + установил bufsize в 1 и сбросил stdin сразу после wrtie. Тем не менее я не получаю выход.

Вторая попытка: Я попробовал это решение, и это работает для меня:

from subprocess import Popen, PIPE 

fw = open("tmpout", "wb") 
fr = open("tmpout", "r") 
p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1) 
p.stdin.write("1\n") 
out = fr.read() 
p.stdin.write("5\n") 
out = fr.read() 
fw.close() 
fr.close() 

ответ

12

Два решения этой проблемы на Linux:

Первый заключается в использовании файла для записи выходных чтобы и читать из него одновременно:

from subprocess import Popen, PIPE 

fw = open("tmpout", "wb") 
fr = open("tmpout", "r") 
p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1) 
p.stdin.write("1\n") 
out = fr.read() 
p.stdin.write("5\n") 
out = fr.read() 
fw.close() 
fr.close() 

Во-вторых, как и предложил JF Себастьян, чтобы сделать p.stdout и p.stderr р ipes non-blocking с использованием модуля fnctl:

import os 
import fcntl 
from subprocess import Popen, PIPE 
def setNonBlocking(fd): 
    """ 
    Set the file description of the given file descriptor to non-blocking. 
    """ 
    flags = fcntl.fcntl(fd, fcntl.F_GETFL) 
    flags = flags | os.O_NONBLOCK 
    fcntl.fcntl(fd, fcntl.F_SETFL, flags) 

p = Popen("./a.out", stdin = PIPE, stdout = PIPE, stderr = PIPE, bufsize = 1) 
setNonBlocking(p.stdout) 
setNonBlocking(p.stderr) 

p.stdin.write("1\n") 
while True: 
    try: 
     out1 = p.stdout.read() 
    except IOError: 
     continue 
    else: 
     break 
out1 = p.stdout.read() 
p.stdin.write("5\n") 
while True: 
    try: 
     out2 = p.stdout.read() 
    except IOError: 
     continue 
    else: 
     break 
+0

Я не совсем понимаю, что вы имеете в виду. Сначала я хотел прочитать только частичный вывод (одна строка) –

+0

Я не понял ваш пример. Что делает эта строка: p = Popen ([sys.executable, "-u", '-c' 'для строки в iter (input, ""): print ("a" * int (строка) * 10 * * 6) '], stdin = PIPE, stdout = PIPE, bufsize = 1) означает? –

+0

Вы правы, я отредактировал свое второе решение, когда использовал его. Проблема заключалась в том, что когда я сначала попробовал решение, я попробовал его на интерпретаторе python (я не писал скрипт, проверял его только вручную), так что он работал (из-за медленного времени реакции пользователя). Когда я попытался написать сценарий, я столкнулся с проблемой, о которой вы упоминали, поэтому я добавил цикл while, пока вход не будет готов. Я фактически получаю весь вывод или ничего вообще (исключение IOError), будет ли полезно, если я загружу исходный код a.out (это программа C)? –

Смежные вопросы