2013-11-09 18 views
12

Ich habe ein Programm, das mit dem Benutzer interagiert (fungiert wie eine Shell), und ich möchte es interaktiv mit Python Subprozessmodul ausführen. Das heißt, ich möchte die Möglichkeit, in stdin schreiben und sofort die Ausgabe von stdout erhalten. Ich habe viele Lösungen ausprobiert, die hier angeboten werden, aber keine von ihnen scheint für meine Bedürfnisse zu funktionieren.Interaktive Eingabe/Ausgabe mit Python

Der Code, den ich habe auf der Grundlage schriftlicher 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) 

Das Problem ist, dass die Warteschlange leer bleibt, als ob es keine Ausgabe. Nur wenn ich alle Eingaben, die das Programm ausführen und beenden muss, an stdin schreibe, bekomme ich die Ausgabe (was ich nicht will). Zum Beispiel wenn ich etwas mache wie:

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

Gibt es eine Möglichkeit zu tun, was ich tun möchte?

EDIT: Das Skript wird auf einer Linux-Maschine ausgeführt. Ich änderte mein Skript und löschte die universal_newlines = True + setzte die bufsize auf 1 und flushed stdin sofort nach wrtie. Trotzdem bekomme ich keine Ausgabe.

Zweiter Versuch: Ich habe versucht, diese Lösung und es funktioniert für mich:

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

Antwort

12

Zwei Lösungen für dieses Problem unter Linux:

Erste ist, eine Datei zu verwenden, um die Ausgabe zu schreiben zu, und gleichzeitig von ihnen lesen:

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

Zweitens, wie JF Sebastian angeboten wird, ist zu p.stdout und p.stderr p zu machen ipes nicht-blocking mit fnctl modul:

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

Ich verstehe nicht genau, was Sie meinen. Was ich zuerst machen wollte, ist nur Teilausgabe zu lesen (eine Zeile) –

+0

Ich habe dein Beispiel nicht verstanden. Was bedeutet diese Zeile: p = Popen ([sys.executable, "-u", '-c' 'für Zeile iner (Eingabe, "")): print ("a" * int (Zeile) * 10 * * 6) '], stdin = PIPE, stdout = PIPE, bufsize = 1) bedeuten? –

+0

Sie haben recht, ich habe meine zweite Lösung bearbeitet, als ich sie benutzt habe. Das Problem war, dass wenn ich die Lösung zuerst ausprobiert habe, habe ich es auf dem Python-Interpreter ausprobiert (ich habe das Skript nicht geschrieben, sondern nur manuell getestet), so dass es funktionierte (aufgrund der menschlichen langsamen Reaktionszeit). Als ich versucht habe, das Skript zu schreiben, bin ich auf das Problem gestoßen, das Sie erwähnt haben, also habe ich die while-Schleife hinzugefügt, bis die Eingabe fertig ist. Ich bekomme tatsächlich die gesamte Ausgabe oder nichts (IOError Ausnahme), wird es hilfreich sein, wenn ich den a.out Quellcode hochlade (es ist ein C Programm)? –