2013-11-09 13 views
12

Ho un programma che interagisce con l'utente (agisce come una shell), e voglio eseguirlo usando il modulo subprocess python in modo interattivo. Ciò significa che voglio la possibilità di scrivere su stdin e ottenere immediatamente l'output da stdout. Ho provato molte soluzioni offerte qui, ma nessuna sembra funzionare per i miei bisogni.Input/output interattivo usando python

Il codice che ho scritto sulla base di 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) 

Il problema è che la coda rimane vuoto, come se non v'è alcuna uscita. Solo se scrivo per stdin tutti gli input che il programma deve eseguire e terminare, ottengo l'output (che non è quello che voglio). Per esempio se faccio qualcosa del tipo:

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

C'è un modo per fare ciò che voglio fare?

MODIFICA: Lo script verrà eseguito su una macchina Linux. Ho cambiato il mio script e cancellato universal_newlines = True + imposta bufsize su 1 e svuota lo stdin immediatamente dopo wrtie. Ancora non ottengo output.

secondo tentativo: ho provato questa soluzione e funziona per me:

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() 

risposta

12

due soluzioni per questo problema su Linux:

primo è quello di utilizzare un file per scrivere l'output a, e leggere da esso simultaneamente:

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() 

in secondo luogo, come ha offerto JF Sebastian, è quello di rendere p.stdout e p.stderr p ipes non-blocking usando il modulo 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

Non capisco esattamente cosa intendi. Quello che volevo fare all'inizio è leggere solo l'output parziale (una riga) –

+0

Non ho capito il tuo esempio. Che cosa significa questa riga: p = Popen ([sys.executable, "-u", '-c' 'per la riga in iter (input, ""): print ("a" * int (riga) * 10 * * 6) '], stdin = PIPE, stdout = PIPE, bufsize = 1) significa? –

+0

Hai ragione, ho modificato la mia seconda soluzione mentre la usavo. Il problema era che quando ho provato la soluzione all'inizio, l'ho provato sull'interprete python (non ho scritto lo script, l'ho testato solo manualmente), quindi ha funzionato (a causa del tempo di risposta lento umano). Quando ho provato a scrivere lo script, ho riscontrato il problema che hai menzionato, quindi ho aggiunto il ciclo while, finché l'input non è pronto. In realtà ottengo l'intero output o nulla (eccezione IOError), sarà utile se carico il codice sorgente a.out (è un programma C)? –